Proyecto en Rails con una base de datos legacy

  • por Sebastián Meza

Cuando empezamos a trabajar en un proyecto, la mayoría de las veces la base de datos suele iniciar literalmente en blanco, lo cual implica realizar las típicas acciones usando las herramientas que rails nos ofrece: generadores, rake tasks (create, migrate, seed), etc. para poder dar forma a las tablas que se van agregando conforme lo necesite el proyecto y sus requerimientos.  

Pero ¿qué pasa cuando tenemos que empezar con un proyecto que ya posee una base de datos legacy, que es tan extensa y/o compleja como para decidir volver a modelarla e implementarla? Una opción, podría ser la creación de los modelos a mano y configurar todos los detalles en cada uno, lo cual es completamente válido, pero se nos haría bastante tedioso si estamos hablando de una gran cantidad de tablas...

 

 

Sin embargo, como la mayoría de las cosas en el mundo de Ruby (y Rails), ya existe una gema que se encarga de ello, esta es RMRE (Rails Models Reverse Engineering), que como su nombre lo dice, utiliza ingeniería inversa a partir de una base de datos existente para generar los modelos de ActiveRecord en nuestro proyecto.

Esta gema, a pesar de estar un tanto descontinuada (su último commit fue en 2015) es soportada sin problemas por Rails 5.1 y a continuación, veremos un ejemplo sencillo de cómo utilizarla.

 

1- La base de datos

Vamos a utilizar una base de datos PostgreSQL existente, que descargaremos desde aquí. Para este ejemplo descargaremos el archivo llamado world que contiene datos de ciudades, países e idiomas de países, es bastante simple, pero servirá para entender el funcionamiento básico de la gema laugh

 

Descomprimimos el archivo y dentro encontraremos un script .sql que ejecutaremos más tarde, ahora toca crear la base desde la línea de comandos. Para esto nos conectamos vía psql:

$ psql
# CREATE DATABASE world;

 

Volvemos a la ubicación de nuestro script descargado y ejecutamos lo siguiente

$ psql -U <tu_usuario_postgres> -d world -f world.sql

 

Si queremos comprobar que todo salió bien, nos conectamos de nuevo y hurgamos un poco los datos.

$ psql
# \c world
// You are now connected to database "world" as user "sebastian".
# \dt

 

El último comando nos mostrará que efectivamente se crearon las tablas.

 

2 - El proyecto de prueba

Primero que todo, instalamos la gema

$ gem install rmre

 

Creamos un proyecto nuevo (en este caso, usando Rails en su versión 5.1.4), y especificamos postgresql como adapter.

$ rails new myApp -d=postgresql

 

Si revisamos la documentación de la gema, su uso es bastante sencillo

$ rmre -a postgresql -d my_database -u my_username -p my_password -o /path/where/models/will/be/created

En donde

  • -a : Corresponde al adapter, el cual soporta varios rdbms (MySQL, SQLServer, Oracle)
  • -d : El nombre de la base de datos
  • -u : el usuario con el que se realizará la conexión a dicha base de datos
  • -p : Su contraseña
  • -o : Ruta donde se crearán los archivos .rb de los modelos.

 

Si tomamos en cuenta nuestro ejemplo, entonces reemplazamos según corresponda

$ rmre -a postgresql -d world -u sebastian -p sebastian -o ~/Documents/dev/myApp/app/models

Y obtenemos

 

El contenido de cada archivo debería ser algo como esto:

# app/models/city.rb

class City < ActiveRecord::Base
    self.table_name = 'city'

    has_many :countries, :class_name => 'Country'
end

 

# app/models/country.rb 

class Country < ActiveRecord::Base
    self.table_name = 'country'
    self.primary_key = :code

    belongs_to :city, :class_name => 'City', :foreign_key => :capital
    has_many :countrylanguages, :class_name => 'Countrylanguage'
end

 

# app/models/countrylanguage.rb 

class Countrylanguage < ActiveRecord::Base
    self.table_name = 'countrylanguage'
    self.primary_key = :countrycode

    belongs_to :country, :class_name => 'Country', :foreign_key => :countrycode
end

Aunque el diseño que posee la base de datos de ejemplo es completamente discutible, no ahondaremos en ello porque ahora pasaremos a testear si efectivamente podemos hacer las querys a través de estos modelos. 

 

Generamos el schema.rb

$ rake db:migrate

 

Lo siguiente es crear un controller con una vista simple que permita, por ejemplo, obtener todas las ciudades.

$ rails g controller pages index

 

Seteamos la ruta como raíz

# config/routes.rb

Rails.application.routes.draw do
  root 'pages#index'
end

 

Y en el controller, traemos los datos

# app/controllers/pages_controller.rb

class PagesController < ApplicationController
  def index
    @cities = City.all
  end
end

Los mostramos en una tabla

# app/views/pages/index.html.erb

<h1>Ciudades</h1>
<table>
  <thead>
    <tr>
      <th>Nombre</th>
      <th>Código país</th>
      <th>Distrito</th>
      <th>Población</th>
    </tr>
  </thead>
  <tbody>
    <% @cities.each do |city| %>
      <tr>
        <td><%= city.name %></td>
        <td><%= city.countrycode %></td>
        <td><%= city.district %></td>
        <td style="text-align: right;"><%= number_with_delimiter(city.population, delimiter: '.') %></td>
      </tr>
    <% end %>
  </tbody>
</table>

Y voilà

 

 

3 - Consideraciones adicionales

Existen otras opciones y configuraciones que vale la pena mencionar, como por ejemplo:

  • Filtrar las tablas a convertir: Para esto la gema considera prefijos, los cuales deben ser especificados de ambas formas 
    -i ec_,vi_
    --include ec_,vi_

    Esto sólo creará los modelos con las tablas que su nombre comience con ec_ y vi_

  • Archivo de configuración: Es posible generar un archivo de configuración, para poder personalizar la manera en que la gema se conectará con la base de datos. Para generar este archivo, sólo hay que ingresar 

    $ rmre -o /tmp/test -f

    Lo que genera 2 archivos: /tmp/test/rmre_db.rb/tmp/test/rmre_db.yml. El primero es un ejemplo de configuración, de cómo los modelos se crearán y cómo se establecerá la conexión hacia la base de datos, esto es bastante útil si se requiere testear antes de utilizar en un proyecto. El segundo es un ejemplo de configuración para la conexión a la base de datos, así, en vez de utilizar el método visto en el ejemplo, simplemente se especifica este archivo: 

    $ rmre -f /tmp/test/rmre_db.yml

 

 

Cabe destacar que la gema no es 100% infalible, habrá que meter mano si es que la base de datos no se adapta a las convenciones de Rails, pero no es más que personalizar el archivo de configuración mencionado anteriormente. 

Aunque puede no ser tan común el encontrarse con proyectos de esta naturaleza, es de mucha utilidad el tener referencias de este tipo de herramientas que podrían simplificar mucho el trabajo de tener que estar realizando todo a mano.

 

Referencias:

https://github.com/bosko/rmre

http://pragdevnotes.com/2010/09/30/rmre-rails-models-reverse-engineering-gem/