Come spedire email in modo asincrono con Devise e Rails3
Ciao a tutti.
Vorrei mostrare un workaround per inviare email in modo asincrono usando Devise e Rails3.
Supponiamo di avere già in piedi la nostra applicazione, con Devise e delayed_job installati correttamente.
Un primo tentativo è stato quello di aggiungere in config/initializers il seguente file devise_async.rb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #devise_async.rb module Devise module Models module Confirmable handle_asynchronously :send_confirmation_instructions end module Recoverable handle_asynchronously :send_reset_password_instructions end module Lockable handle_asynchronously :send_unlock_instructions end end end |
Questo workaround ha funzionato parzialmente: ha messo in coda correttamente l’invio della mail di confirmation nella tabella di delayed_job ma il problema si è poi rivelato al momento dell’attivazione del job, ottenendo questo errore:
1 2 | User#send_confirmation_instructions_without_delay failed with NoMethodError: undefined method 'send_confirmation_instructions_without_delay' for #<User:0x000000032f87c8> - 1 failed attempts [Worker(host:stefano-desktop pid:13153)] |
Come si puo’ notare, il job sta tentando di chiamare il metodo sbagliato di invio istruzioni, send_confirmation_instructions_without_delay.
A questo punto ho implementato un hack ancora più sporco, overridando i metodi in questione e seguendo la sintassi indicata da intridea per inviare le email in background:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #devise_async.rb module Devise module Models module Confirmable # Send confirmation instructions by email def send_confirmation_instructions generate_confirmation_token! if self.confirmation_token.nil? ::Devise.mailer.delay.confirmation_instructions(self) end end module Recoverable # Resets reset password token and send reset password instructions by email def send_reset_password_instructions generate_reset_password_token! ::Devise.mailer.delay.reset_password_instructions(self) end end module Lockable # Send unlock instructions by email def send_unlock_instructions ::Devise.mailer.delay.unlock_instructions(self) end end end end |
Questo metodo è troppo legato però all’implementazione di Devise e non rappresenta quindi una valida soluzione (oltre ad essere veramente di pessima fattura).
L’ultima idea, che poi rappresenta la soluzione adottata almeno fino a qualcosa di meglio, è la seguente: utilizzare alias_method in questo modo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #devise_async.rb module Devise module Models module Confirmable alias_method :send_confirmation_instructions_without_delay, :send_confirmation_instructions handle_asynchronously :send_confirmation_instructions end module Recoverable alias_method :send_reset_password_instructions_without_delay, :send_reset_password_instructions handle_asynchronously :send_reset_password_instructions end module Lockable alias_method :send_unlock_instructions_without_delay, :send_unlock_instructions handle_asynchronously :send_unlock_instructions end end end |
Questo ultimo hack funziona a meraviglia, non è il massimo ma permette di inviare mail da Devise in modo asincrono.
Se avete altre soluzioni migliori, non esitate e condividerle.
Tags: asynchronous, delayed_job, devise, rails, rails3, ruby on rails

















[...] ORIGINAL POST [...]
Thanks, this was helpful. For Devise::Invitable:
module Devise
module Models
module Invitable
alias_method :deliver_invitation_without_delay, :deliver_invitation
handle_asynchronously :deliver_invitation
end
end
end
Awesome, worked like a charm! Thank you so much.
Thank you! Worked fantastic, practically out-of-the-box solution!
This works in the UI, but my integration test fails. I notice that the whole method of send_confirmation_instructions runs as a delayed job, but I really just want to send the e-mail as the delayed job. I.e., I want to generate the token and save the user as part of the controller invocation. Thoughts?