Las aventuras de @Jmlevick en el bizarro mundo de las bases de datos no relacionales


NOTAS: Para quien no sepa, @Jmlevick es mi usuario en Twitter... Para poder entender este post, es necesario que al menos hayas leído este otro y entiendas que el objeto "kid" en los callbacks de abajo se refiere a un solo niño en nuestra colección/modelo de kid llamado (por ejemplo) con una querie como Kid.all[0] por ejemplo.

Fíjense que ahorita estoy trabajando en un proyecto Rails muy interesante que requiere relaciones de modelo algo complejas. Por cuestiones de flexibilidad opté por el uso de una poderosa base de datos no relacional llamada MongoDB cosa que me ha traído bastante aprendizaje. Hace poco ya les describí cómo se hace una relación has_many :through en Mongoid y ese método funciona perfecto, ¡es el indicado! el problema está en que todo lo que suelta son Arrays. Mongoid (el ammm, "driver" de Rails para MongoDB) es algo bastante poderoso pero también bizarro en situaciones como las que explico arriba. Al ser todo Arrays, es diferente  la manipulación de la data y por lo tanto, algunas cosas a las que estarías acostumbrado a utilizar en una DB relacional no funcionan bien (o más bien, no funcionan igual).

En mi caso específico, me topé con que un callback que tengo en algún lugar ya no funcionaba, porque los métodos que ocupaba comúnmente no eran llamables sobre objetos array.

So, ¿todo en Ruby es un objeto, no?  ya no estaba manipulando objetos standalone, pero eso no quería decir que los valores dentro de un objeto array no sean también objetos. Y es aquí donde nuestro viaje comienza:

En Mongoid con el tipo de relación que ocupé ya no manejas objetos interrelacionados como tal, sino arrays... Lo primero que hice para poder hacer que todo funcionara, fue OCUPAR LA CONSOLA DE RAILS, (Créeme, es tu amiga!) Me fijé cómo es que los objetos se interrelacionaban, qué métodos se podían llamar sobre ellos y demás. (esto último se logra con el método instance_methods por si no lo sabías) y tras comprender más o menos qué onda (y la lógica de mi aplicación), decidí empezar a hacer pruebas, NO EN LA APP física (es decir, no en sus vistas o modelos) sino en la consola. Fueron  como 4 horas de prueba y error, donde minuto a minuto sentía que me acercaba más al resultado, al esclarecimiento, la iluminación jajaja.

No recuerdo mucho de lo que sucedió en todas mis pruebas, pero tratando de ser fiel a mi procedimiento puedo ennumerar las dferentes versiones de mi callback acá abajo:

1) Comprendí que debía iterar

Como dije, (y estaba en lo cierto) todos los objetos dentro de un objeto array son objetos individuales... Esto quiere decir que si lograba sacarlos del array, podía llamar sobre c/u los métodos que quisiera, (aquellos que no eran aplicables sobre arrays pues) y para empezar el callback se veía así:

El problema con este tipo de callback es que está "hard-coded" esto quiere decir que ocupa que le digamos explícitamente los objetos a manipular... Esto es malo si vas a manipular varios objetos o vas a añadir más después, sin embargo cumplía su cometido; Pero no era lo que buscaba...

2) Comprendí que debía accesar a los 2 arrays

Cuando capté esto, lo que hice fue hacer 2 loops distintos que iteraran sobre los 2 arrays que estaba manejando en mi relación, de tal manera que pudiera accesar a los valores individuales de cada uno en el callback... No recuerdo muy bien como estuvo este, pero si no me equivoco era algo así:

Este efectivamente itera sobre los 2 arrays y me permite trabajar con los objetos individuales de c/u (de manera prácticamente automática, cosa que requería) pero, el problema era que las operaciones a realizar tras iterar las hacía 2 veces! (supongo que por la doble iteración) So, tampoco era la respuesta.

3) Finalmente, Entendí

Al final, tras probar ambos arrays, comprendí que efectivamente debía iterar para poder trabajar con los objetos individuales, pero como en este caso estoy ocupando 2 arrays (e iterar sobre c/u en el desarrollo de la función me daba un comportamiento errático como explico en el caso 2) comprendí que efectivamente debía iterar sobre ambos arrays pero en una sola pasada... El método que al final generó el callback correcto fue:

Y este sí que funcionó según lo esperado. Aquí, tengo que aclarar que ocupé bastantes búsquedas en google y pruebas/error (además de leídas a la documentación de la clase array de ruby) para comprender cómo se podía lograr lo que esperaba y refactorizar mi código exitosamente.

¡Así que ya saben! En Mongoid, cuando se encuentren en situaciones donde los objetos a manipular se encuentran en arrays, hay que iterar y poner a trabajar el código eficientemente.

P.D. Disculpas por saltarme las indentaciones adecuadas en los snippets