💎 Rails: Carrierwave & after_create

Carrierwave devuelve la ruta temporal del archivo y no la ruta final al acceder a ella luego del callback after_create.

💎 Rails: Carrierwave & after_create

El día de ahora me tocó desarrollar un feature bastante sencillo:

Crear un registro de usuario, agregarle una imagen justo antes del create y luego del create, enviar un correo con dicha imagen.

ActiveRecord Model + Carrierwave Uploader + Mailer + active record callbacks = 💁‍♂️💰

Algo más o menos así:

class User < ApplicationRecord
  include ::RandomStringGenerator
  include ::QrGenerator
 
  mount_uploader :qr_image, QrUploader

  before_create :generate_qr_code
  before_create :generate_qr_image
  after_create  :send_welcome_mailer

  private

  def generate_qr_code
    self.qr_code = generate_random_string
  end

  def generate_qr_image
    self.qr_image = generate_qr(self.qr_code)
  end

  def send_welcome_mailer
    UserMailer.welcome(self).deliver_now
  end
end

Resulta que no funcionó. La imagen devolvía la ruta temporal del archivo dentro del email y no la ruta final declarada en el uploader de carrierwave 🤦‍♂️

'uploads/tmp/1556231365-22823-0005-8587/EGR3FK.png'

Resulta que el after_create no es tan right after como uno puede pensar. Dice la documentación:

"Note that this callback is still wrapped in the transaction around save."

Es decir, no se puede asegurar con los ojos cerrados que el registro quedó completamente creado, a pesar que el nombre del callback hace creer lo contrario. El after_create no asegura que el registro verdaderamente se haya persistido por completo.

Pero no hay bug que dure 100 años ni otro callback que no lo componga:

after_commit(*args, &block)public
This callback is called after a record has been created, updated, or destroyed.
You can specify that the callback should only be fired by a certain action with the :on option.

Así que un pequeño refactor, et voilà:

class User < ApplicationRecord
  
  ...

  before_create :generate_qr_code
  before_create :generate_qr_image
  after_commit  :send_welcome_mailer, on: :create

  ...

end

Se obtiene la ruta correcta:

'uploads/user/qr_image/11/MFZ48YZD.png'