Automatización de Inserción de Datos en Rails con db:seed

Clase 30 de 36Curso de Introducción a Ruby on Rails

Objetivo

Una vez hemos construído el diseño base de nuestra base datos podemos crear un algoritmo para la inserción de registros a la base de datos de forma automática, a este proceso se le conoce como seeding (pronunciado [SI DIN] /ˈsiːdɪŋ/) de base de datos, el objetivo de esta clase, es crear esa estructura inicial de información que nos permita ingresar usuarios, tareas, categorías, participantes, haciendo uso de la tarea de rails rails db:seed que explicaremos más adelante.

rails db:seed

Rails, como habíamos visto tiene varios comandos que ejecutados desde la consola de comandos pueden generar o crear archivos y directorios que cumplen un propósito, para este caso, debemos introducir el concepto de tareas, las cuales son procesos preconfigurados que ejecutan una secuencia de instrucciones sobre algún área en particular, en las anteriores clases hemos interactuado con algunas de ellas, rails db:migrate es un buen ejemplo, esta tarea nos ayuda a ejecutar las migraciones pendientes de base datos; ahora, es el turno para rails db:seed tarea que usando el contenido de un archivo semilla ubicado en la dirección db/seeds.rb creará los registros que nosotros allí coloquemos.

Estructurando nuestro archivo semilla

Vamos a editar el contenido del archivo db/seeds.rb, primero eliminaremos todo su contenido, y empezaremos a agregar un conjunto de líneas de código

Creando usuarios

Dentro del archivo db/seeds.rb vamos a generar un arreglo de nombres

# db/seeds.rb ['juan', 'andrea', 'leon', 'andres', 'natalia', 'camilo', 'rusbel', 'johan']

Luego vamos a iterar ese arreglo (recorrer elemento por elemento para hacer algo con cada uno)

['juan', 'andrea', 'leon', 'andres', 'natalia', 'camilo', 'rusbel', 'johan'].each do |name| # la linea de creacion va aquí end

Para finalmente, dentro del iterador usar el método create de la clase/modelo User, con el objetivo de crear un usuario, de esta forma, y recordando al modelo User, necesitamos los campos email y password para poder crear un nuevo usuario, asi que usaremos cada elemento del arreglo para formar un email utilizando interpolación de texto ”#{}” y usaremos la misma contraseña para todos 123456. Al final del iterador colocaremos un mensaje de texto mencionando que los usuarios han sido creados con éxito, todo este conjunto de pasos se verían de la siguiente forma en el archivo.

['juan', 'andrea', 'leon', 'andres', 'natalia', 'camilo', 'rusbel', 'johan'].each do |name| User.create email: "#{name}@platzi.com", password: '123456' end puts 'Users has been created'

Creando categorías

Similar al proceso de creación de usuarios también crearemos las categorías, usando un arreglo con el nombre de las mismas, y utilizando una descripción de prueba con la cadena ”--”

['desarrollo', 'mercadeo', 'conceptualización', 'ejercicios'].each do |name| Category.create name: name, description: '--' end puts 'Categories has been created'

Creando Tareas y Participantes

Este es el proceso más importante de nuestros datos semilla, vamos a usar una estructura basada en arreglos anidados para obtener toda la información que nuestra tarea y participantes necesitan, debo aclararte que esta estructura es una de las muchas formas que tu puedes encontrar para conseguir el mismo objetivo, es decir crear los registros automáticamente.

Una tarea necesita una categoría y un nombre, así que podemos usar las estrategias anteriores para crearla, sin embargo, para que las validaciones nos dejen seguir, necesitamos también asociar a la tarea un propietario y un conjunto de participantes.

El propietario es sencillo de relacionar, simplemente voy a tomar el usuario de nombre johan y haré una referencia hacia él con una variable local, Johan será el propietario de todas las tareas, para buscar y almacenar la referencia del propietario usaremos el método find_by y buscaremos una coincidencia por email dentro de los registros de usuarios previamente creados:

owner = User.find_by(email: 'johan@platzi.com')

Para asociar la descripción y la categoría de la tarea podríamos usar un arreglo bidimensional, algo como esto:

[ ['conceptualización', 'Bienvenida'] ['conceptualización', '¿Qué es ruby on rails y por qué usarlo?'] ['conceptualización', 'Entorno de desarrollo de RoR'] ['ejercicios', 'Instalación de Ruby, RoR en windows y Linux'] ]

No debes copiar estas líneas de los últimos dos bloques en el archivo, porque apenas estamos construyendo la idea de creación, y además sólo hemos resuelto parcialmente el problema; necesitamos una forma para pasar los participantes, teniendo en cuenta que cada participante tiene un Nombre y un Rol (responsable, o seguidor). Para conseguir esto vamos a usar otro arreglo más al mismo nivel de los elementos categoría y nombre del bloque anterior, sin embargo, para mejorar la visualización vamos a usar varias líneas para representar nuestros arreglos anidados y además para continuar con la explicación de una forma más sencilla vamos a usar una variable local de nombre base que almacenará nuestro arreglo contenedor de prueba.

base = [ [ 'conceptualización', 'Bienvenida ', ['johan:1', 'leon:2', 'andrea:random'] ], [ 'conceptualización', '¿Qué es ruby on rails y por qué usarlo?', ['johan:1', 'leon:2', 'andrea:random'] ], [ 'conceptualización', 'Entorno de desarrollo de RoR', ['johan:1', 'leon:2', 'andrea:random'] ], [ 'ejercicios', 'Instalación de Ruby, RoR en windows y Linux', ['johan:1', 'leon:2', 'andrea:random'] ], ]

En el bloque anterior de código, podemos ver cómo hemos integrado un tercer elemento más, que resulta ser un nuevo arreglo, como lo vemos en el bloque de abajo; este arreglo resulta ser una convención que he elegido para referenciar un nombre de un usuario junto con su rol. Por ejemplo, johan:1 significa que el participante se llama Johan, y que su rol será 1, es decir responsable. Cuando el rol sea random vamos a usar un sistema de selección aleatoria de Ruby sobre alguno de los dos tipos de roles: 1 - responsable, 2 - seguidor

['johan:1', 'leon:2', 'andrea:random']

Vamos a ver cómo manipular este nuevo arreglo, asignemos una variable local llamada my_array al arreglo de arriba, y sigamos la siguiente secuencia:

my_array = ['johan:1', 'leon:2', 'andrea:random']

vamos a tomar, cada uno de los elementos de ese arreglo y asignarlos a tres variables locales llamadas: first, second, third (podrías usar cualquier nombre en realidad). Este proceso se llama parallel assignment (pronunciado [paralel asainmen] /ˈperəˌlel əˈsaɪnmənt/) tenlo muy presente porque lo usaremos más adelante en el código final, el parallel assignment nos permite asignar de forma conmutada valores en secuencia del arreglo en el mismo orden en el que las variables serán definidas

first, second, third = my_array

Así, si evaluamos las tres variables usando la consola de rails, tendríamos lo siguiente

~ rc > my_array = ['johan:1', 'leon:2', 'andrea:random'] > first, second, third = my_array > first #=> 'johan:1' > second #=> 'leon:2' > third #=> 'andrea:random'

Te darás cuenta, que incluso no es necesaria la variable my_array y podrás hacer parallel assignment directamente con la declaración del arreglo, de esta forma

first, second, third = ['johan:1', 'leon:2', 'andrea:random']

Una vez teniendo la referencia de cada uno de los tres participantes podemos usar el método split para obtener el nombre y rol, para el caso del primer elemento lo podríamos experimentar en la consola de rails (yendo a la raíz del proyecto en la consola) de la siguiente forma:

➜ organizador ~ rc > first, second, third = ['johan:1', 'leon:2', 'andrea:random'] > first.split(:) #=> ["johan", "1"]

Por lo que podemos usar parallel assignment tal que tengamos las variables name y raw_rol:

name, raw_role = first.split(:)

Para el caso de la variable third que tiene el rol random usaremos el método sample de la clase Array para aleatoriamente seleccionar un elemento dentro del conjunto [1, 2], así que si quisiéramos iterar el arreglo de participantes incluyendo el término random debemos usar un if en su forma ternaria y el método sample de la siguiente forma:

['johan:1', 'leon:2', 'andrea:random'].each do |participant| name, raw_role = participant.split(:) rol = raw_role == ‘random’ ? [1, 2].sample : raw_role end

Por último, para terminar nuestro experimento vamos a tomar el arreglo de prueba que habíamos definido antes llamado base, lo iteraremos para construir dentro de la misma iteración, un conjunto de participantes no persistidos en la base de datos haciendo uso del método new de la clase/modelo Participant, este conjunto se llamará participants

# la asignación de elementos del `each` es similar a la del parallel assignment, por eso # podemos usar `category, description, participant_set` como elementos de la iteración base.each do |category, description, participant_set| participants = participant_set.map do |participant| user_name, raw_role = participant.split(':') role = raw_role == 'random' ? [1, 2].sample : raw_role Participant.new( user: User.find_by(email: "#{user_name}@platzi.com"), role: role.to_i ) end end

Teniendo los participantes previamente construidos, dentro de la misma iteración crearemos nuestras tareas usando un nombre consecutivo, la descripción del arreglo, la categoría del arreglo y una fecha de vencimiento de 15 días a partir de la creación de la tarea. Complementando el bloque de la siguiente manera:

base.each do |category, description, participant_set| participants = participant_set.map do |participant| user_name, raw_role = participant.split(':') role = raw_role == 'random' ? [1, 2].sample : raw_role Participant.new( user: User.find_by(email: "#{user_name}@platzi.com"), role: role.to_i ) end # El nuevo código es el que está abajo Task.create!( category: Category.find_by(name: category), name: "Tarea ##{Task.count + 1}", description: description, due_date: Date.today + 15.days, owner: owner, participating_users: participants ) end puts 'Tasks has been created'

Una vez entendido todo el proceso, el código completo del archivo db:seed será el siguiente:

['juan', 'andrea', 'leon', 'andres', 'natalia', 'camilo', 'rusbel', 'johan'].each do |name| User.create email: "#{name}@platzi.com", password: '123456' end puts 'Users has been created' ['desarrollo', 'mercadeo', 'conceptualización', 'ejercicios'].each do |name| Category.create name: name, description: '--' end puts 'Categories has been created' owner = User.find_by(email: 'johan@platzi.com') [ ['conceptualización', 'Bienvenida ', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', '¿Qué es ruby on rails y por qué usarlo?', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Entorno de desarrollo de RoR', ['juan:1', 'leon:2', 'andrea:random']], ['ejercicios', 'Instalación de Ruby, RoR en windows y Linux', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Entender la web con rieles', ['juan:1', 'leon:2', 'andrea:random']], ['ejercicios', 'Crear una nueva aplicación RoR ¡Hola Rails!', ['juan:1', 'leon:2', 'andrea:random']], ['ejercicios', 'Manipular el patrón MVC', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', '¿Qué vamos a desarrollar?', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Crear la base de nuestra aplicación', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Los secretos de rails', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Assets y Layouts', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Diseñar el modelo de datos', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Agregar primer conjunto de scaffolds', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Cómo entender las migraciones', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Esteroides para tu desarrollo - HAML', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Esteroides para tu desarrollo - Simple Form', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Regenerando el primer conjunto de scaffolds', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Internacionalización de tu aplicación', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Esteroides para tu desarrollo - Debugging', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Agregar validaciones de modelo', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Añadiendo el concepto de usuario', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Añadir participantes a la tarea', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'CanCanCan ¿puedes hacerlo?', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Callbacks en Rails', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Enviar email a los participantes', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Añadir comentarios vía AJAX', ['juan:1', 'leon:2', 'andrea:random']], ['desarrollo', 'Embellecer nuestra aplicación', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Desplegando a Heroku', ['juan:1', 'leon:2', 'andrea:random']], ['conceptualización', 'Conclusiones del curso', ['juan:1', 'leon:2', 'andrea:random']], ].each do |category, description, participant_set| participants = participant_set.map do |participant| user_name, raw_role = participant.split(':') role = raw_role == 'random' ? [1, 2].sample : raw_role Participant.new( user: User.find_by(email: "#{user_name}@platzi.com"), role: role.to_i ) end Task.create!( category: Category.find_by(name: category), name: "Tarea ##{Task.count + 1}", description: description, due_date: Date.today + 15.days, owner: owner, participating_users: participants ) end puts 'Tasks has been created'