- El código que hice hace seis meses, cuando mis conocimientos eran inferiores a los actuales [fácil]
- El código que no he echo yo, sino un compañero, otro equipo o una empresa de terceros (en este orden) [moderado]
- El código o aplicación que ha estado o está funcionando desde hace "tiempo" con una tecnología distinta a rails, distinguiendo:
- Aplicación a recaer [moderado]
- Aplicación a heredar (los datos, el funcionamiento) o convivir [difícil]
- Si no tengo test añadirlos, si tengo entenderlos, añadir más tests... he dicho test? ¡Pues eso, test, test, test!
- Refactoring a demanda, cuando se tenga que modificar algo se aprovecha y se corrige.
¿Rails es legacy?
Si!
¿Rails está pensado para legacy?
En mi opinión, no! La filosofía core de rails es "Convention over Configuration (CoC)". Una aplicación legacy raramente seguirá el convenio de rails, con lo que a priori ya perdemos o tenemos que añadir el coste de ir especificando todo lo que no siga el convenio, nombres de tablas, tipos, joins, etc... Lo segundo y derivado de este problema, es que al salirnos del core de rails y ampliar con gems, nos encontraremos que algunas de ellas están solo pensadas para aplicaciones que sigan el convenio y no tienen toda la especificación rails implementada. Si no tienes alternativa sigue leyendo.
Primera solución: mapeando con vistas
Como: creando vistas que renombren la tabla, las columna y los tipos al convenio de rails y luego creando modelos rails que nos permitan acceder a ellos.
Limitaciones: solo lectura (* dependiendo del SGBD).
Problemas habituales y posibles soluciones / alternativas:
- Indice compuesto por strings:
- Mantenemos el string como indice (nos saltamos el convenio)
- Creamos una tabla intermedia que traduzca de strings a integer y ligamos su mantenimiento con trigers.
- Creamos una función que traduzca de strings a integers (afecta al rendimiento y puedes tener claves muy largas)
- Indice compuesto por múltiples columnas: juntamos las columans en la vista
Segunda solución: mapeado en modelos las cuatro coas más importantes
En el modelo:
nombre de la tabla personalizado:
set_table_name "client"
nombre del identificador principal o clave primaria (sin clave compuesta)
set_primary_key "client_id"nombre del identificador principal o clave primaria (con clave compuesta)
* Tener en cuenta que la opción "find_by_id" deja de funcionar, podemos utilizar "find_by"o "find_by_client_id"
Instal·lamos la gem composite_primary_keysrenombrado columnas puntuales
Especificamos: "set_primary_keys :person_id, :group_id"
alias_attibute "person_name_full", "name"definiendo relaciones con otros modelos
* Aquí los find funcionan para el alias.
has_many :comments,definiendo relaciones con otros modelos y claves compuestas:
:class_name => "WordpressComment"
:foreign_key => "comment_post_ID"
* Ejemplo real: tabla "post" con la columna "comment_post_ID" que mapeamos contra la tabla del modelo "WordpressComment" con el nombre de tabla y el id que tenga definidos.
Si la cosa se complica mucho, mi recomendación es utilizar métodos dentro del modelo que nos permitan realizar las búsquedas:Forzar los plurales "config/initializers/inflections.rb":
def commentsWordpressComment.find_by_post_id(self.coment_post_id)end
* Partiendo del nombre de la tabla y añadiendo una "s"
inflect.irregular 'imp_person', 'imp_persons'En las migrations:
inflect.irregular 'imp_comments', 'imp_commentss'
Siempre que quieras crearlas (lo normal es que venga hechas).
si el campo id no existe, especificarlo
def changesi no hay fechas de control (eliminarlas)
create_table :imp_person, {:id => false} do |t|
...
si tenemos clave primaria añadirle el indice con executet.timestamps
def up
execute "ALTER TABLE person ADD PRIMARY KEY (national_document);"
end
def down
execute "ALTER TABLE person DROP PRIMARY KEY (national_document);"
end
Un par de recomendacoines más:
- Valorar el añadir un prefijo a los modelos, de esta forma juntamos toda la lógica.
- Dado el coste de definir, lo definimos todo, aún siguiendo el convenio de rails, té evitarás problemas raros.
- Si has generado el modelo con scaffold reviste las fixtures, es posible que no te sirvan (y escribe test!).
Otro escenario habitual en legacy: nueva aplicación que tiene que heredar los datos del sistema anterior. Aquí es habitual encontrarse con los mismos problemas de convenio, más las limitaciones derivadas de los nuevos modelos de datos, campos que se añaden, campos que se quitan, nuevas reglas de negocio que pueden provocar que nuestros datos no se validen, nuevas columnas que no podemos rellenar etc... La solución dependerá mucho de nuestro escenario, en general suele dar muy buen resultado crear modelos paralelos más o menos ligados con el principal, por ejemplo podemos tener el modelo "Client" y "ClientLegacy", aquí ya es la habilidad de cada uno para reducir código, solo una nota, en mi opinión es mejor mantener los dos modelos con dos tablas distintas (para mi la validación en BD es igual de importante que en el modelo y claramente ambos tendrán validaciones distintas).
Hasta aquí esta primera aproximación al mundo legacy, en futuros artículos más escenarios, soluciones y experiencias.