En otras ocasiones hemos hablado de las migraciones de Rails, una característica que nos permite modificar el esquema de la base de datos usando Ruby.
Alguien se puede preguntar cuáles son las ventajas reales de manipular la base de datos usando Ruby en lugar de SQL. La primera de ellas es que sólo tendremos que lidiar con las peculiaridades de Ruby, en vez de tener que andar consultando las referencias del dialecto de SQL que haya tenido a bien implementar el fabricante de nuestro gestor de bases de datos. La segunda es que, además de modificar el esquema de la base de datos añadiendo tablas, columnas, etc. con las migraciones podemos modificar los datos ya existentes en nuestras tablas.
Supongamos que tenemos la tabla que vendría dada por la siguiente migración:
1 2 3 4 5 6 7 8 9 10 11 |
class Inicial < ActiveRecord::Migration def self.up create_table :entradas do |t| t.column :voz, :string t.column :inicial, :char t.column :texto, :text end end def self.down end |
Se trata de crear una tabla que nos sirva de diccionario, donde cada voz o palabra tiene un texto asociado pero además una inicial que será el campo que usaremos a la hora de buscar. La inicial, por definición, es la primera letra de la voz.
Pues bien, este esquema para nuestro diccionario tiene un problema: en un diccionario en castellano deberíamos poder buscar por las letras ll y ch. Al haber usado sólo un carácter para la columna inicial no podemos distinguir entre las palabras que empiezan por l y por ll.
Migraciones al rescate: no sólo vamos a cambiar la columna de inicial la base de datos, que pasará a ser una cadena en lugar de un sólo carácter, sino que además rellenaremos esa columan correctamente, comprobando si la voz empieza por ch, por ll o por cualquier otra letra:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class LetrasPorCadenas < ActiveRecord::Migration def self.dame_inicial str pat = /^(ch|ll|[^ ])/ refs = pat.match str refs[0] end def self.up remove_column :entradas, :inicial add_column :entradas, :inicial, :string Entrada.reset_column_information say_with_time "Genero las iniciales" do Entrada.find(:all).each do |e| e.inicial = dame_inicial(e.voz) e.save end end end end |
Son a destacar:
- El útil metodo
say_with_timeque nos sirve para rastrear el progreso de nuestra migración - Tras modificar cada registro hay que invocar el método
savedeActiveRecord, o de lo contrario la tupla no se modificará—no he visto mención a este detalle en ninguno de los tutoriales de migraciones que he encontrado por ahí.
No intentes casar expresiones regulares usando tu dialecto favorito de SQL. Tu salud mental te lo agradecerá.


March 28th, 2006 at 03:50 PM Tienes razón, en ninguno de los tutoriales menciona lo del save y es importante. Yo estaba generando un hash para guardar en una columna y me dió mucho trabajo darme cuenta que faltaba el save.
March 29th, 2006 at 10:41 AM No entiendo qué tiene que ver el save y las migraciones. En este caso, uno está cogiendo objetos normales ActiveRecord y los está modificando. Por tanto, habrá que llamar al save cada vez, ¿no? :-?? Quiero decir, que no es nada específico de migraciones, no veo por qué lo deberían nombrar ahí...
March 29th, 2006 at 10:47 AM Esteban: totalmente de acuerdo, es el comportamiento que debería tener. Sin embargo, si te miras "la documentación de las migraciones":http://api.rubyonrails.com/classes/ActiveRecord/Migration.html, en el apartado "Using a model after changing its table", fíjate que se calcula el campo @salary@ de cada registro de la tabla @Person@... ¡pero no se hace un @save@! Vamos, que como ejemplo de migración que modifica datos creo que ese fragmento de código no sólo no ayuda sino que despista un poco.
March 29th, 2006 at 11:17 AM Epaminondas: ¡Aah, la leche! No lo había visto. Lo único que había visto de la documentación de migraciones era la parte de modificar la estructura de las tablas, y no me había fijado en ese ejemplo. Veo que a veces es mejor _no_ leer la documentación, y actuar por intuición :-P