Interceptar eventos en los modelos
¿Qué son los Callback?
Un callback es un método que se invoca automáticamente al realizarse alguna acción CRUD sobre un modelo (es decir, su clase es ActiveRecord).
Llevándolo a un plano de programación en base de datos, un callback vendría siendo lo que se conoce como un trigger.
Necesidad
Muy temprano en el desarrollo de la aplicación de la que trata este blog, necesité que antes de guardar un objeto en persistencia un atributo de dicho objeto quedara con un llave aleatoria de 50 caracteres.
La necesidad de esto es que luego debía enviar un email donde aparecía un link al sistema que realizaría un tarea fundamental de confirmar la nueva calificación.
Enviar el link con el id del objeto no era una opción porque al ser secuencial, fácilmente un bot podía confirmar todas las nuevas calificaciones!
Implementación
Me remití entonces a escribir un método dentro del modelo NuevaCalificacion, que generara la llave aleatoria...
# genera una llave aleatoria de 50 caracteres para utilizar en la confirmación
# esta llave se utiliza para evitar poner al público el id de la nueva calificación
def generar_llave_confirmacion
caracteres = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
llave_aleatoria = ""
1.upto(50) { |i| llave_aleatoria << caracteres[rand(caracteres.size)] }
return llave_aleatoria
end
Luego de este método, cree un método que asigna realmente la llave aleatoria generada al atributo llave_confirmacion ...
# pone los datos por defecto
def poner_datos_defecto
self.fecha_creacion = Time.now
# asegura que se genere una llave de confirmación que no exista (muy baja probabilidad, pero Murphy existe)
begin
llave = generar_llave_confirmacion
end while NuevaCalificacion.find_by_llave_confirmacion(llave)
self.llave_confirmacion = generar_llave_confirmacion
end
En realidad, el método poner_datos_defecto, no se encarga solamente de dar una llave aleatoría a nuestra nueva calificación, sino que también pone la fecha en otro atributo.
Poner la fecha me gustó mas así, que utilizar la características automática de Rails porque queda implícito para un futuro desarrollador y no tengo que dejar un objeto con referencias en inglés y otras en castellano.
Finalmente debe indicársele al modelo que el método poner_datos_defecto, es un callback que debe ser invocado justo antes de guardar una nueva calificación.
class NuevaCalificacion < ActiveRecord::Base
# ... codigo
before_save :poner_datos_defecto
# .. codigo (incluidos métodos poner_datos_defecto y generar_llave_confirmacion
end
Eso fue todo...
Así cada vez que se invoca el método save en un objeto cuya clase es NuevaCalificacion, aseguramos que fecha_creacion y llave_confirmacion, referencien automáticamente objetos por defecto.
Saludos!