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: , , , , ,


About Stefano

Stefano Mancini is a co-founder of DevInterface.

After graduating in Computer Science, he first specialized in Java/J2EE development by participating in several international projects in the pharmaceutical and banking ambits.

Enthusiast of agile development, like SCRUM for project management and eXtreme Programming for code writing, he then moved to dynamic languages like Ruby and Python.

About DevInterface

We are an information and communication technology agency. Our mission is to provide web application development, design services and communication strategies. We specialize in building web applications with modern and efficient frameworks.

Related Post

5 Responses to “Come spedire email in modo asincrono con Devise e Rails3”

  1. dB. scrive:

    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

  2. Anurag scrive:

    Awesome, worked like a charm! Thank you so much.

  3. Piotrek scrive:

    Thank you! Worked fantastic, practically out-of-the-box solution!

  4. Justin Gordon scrive:

    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?

Leave a Reply

Insert code beetween <code lang="ruby"> and </code>

Copyright 2012 DevInterface s.n.c.

DevInterface Blog is proudly powered by WordPress