2016-03-21 14 views
7

Sto utilizzando un callback after_action nei miei mailer per registrare che l'email è stata inviata. Le e-mail vengono inviate attraverso il lavoro differito. Funziona, tranne quando non siamo in grado di raggiungere il server remoto - nel qual caso, l'e-mail non viene inviata, ma registriamo che lo era. Il lavoro ritardato ripete l'e-mail in un secondo momento e viene consegnato correttamente, ma abbiamo registrato che sono state inviate due e-mail.Rileva errori di consegna del mailer di azione in callback after_action

Sembra qualcosa di simile a questo:

class UserMailer < ActionMailer::Base 

    after_action :record_email 

    def record_email 
    Rails.logger.info("XYZZY: Recording Email") 
    @user.emails.create! 
    end 

    def spam!(user) 
    @user = user 
    Rails.logger.info("XYZZY: Sending spam!") 
    m = mail(to: user.email, subject: 'SPAM!') 
    Rails.logger.info("XYZZY: mail method finished") 
    m 
    end 
end 

Io chiamo questo codice come questo (usando delayed job performable mailer):

UserMailer.delay.spam!(User.find(1)) 

Quando faccio un passo attraverso questo in un debugger, sembra che il mio metodo after_action si chiama prima che la posta sia consegnata

[Job:104580969] XYZZY: Sending spam! 
[Job:104580969] XYZZY: mail method finished 
[Job:104580969] XYZZY: Recording Email 
Job UserMailer.app_registration_welcome (id=104580969) FAILED (3 prior attempts) with Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 1025 

Come posso rilevare errori di rete nel mio metodi di mailing e registrare che il tentativo di e-mail non è riuscito, o non fare nulla? Sto usando Rails 4.2.4.

risposta

2

Questo è quello che mi è venuta, mi piacerebbe avere un modo migliore.

ho usato il callback consegna Mail:

delivery_callback.rb

class DeliveryCallback 
    def delivered_email(mail) 
    data = mail.instance_variable_get(:@_callback_data) 
    unless data.nil? 
     data[:user].email.create! 
    end 
    end 
end 

config/inizializza/mail.rb

Mail.register_observer(DeliveryCallback.new) 

E ho sostituito il mio metodo record_email:

class UserMailer < ActionMailer::Base 

    after_action :record_email 

    def record_email 
    @_message.instance_variable_set(:@_callback_data, {:user => user}) 
    end 
end 

Questo sembra funzionare, se il server remoto non è disponibile, il callback delivery_email non viene richiamato.

C'è un modo migliore!?!?

-1

provare quanto segue:

class UserMailer < ActionMailer::Base 

    # after_action :record_email 

    def record_email 
    Rails.logger.info("XYZZY: Recording Email") 
    @user.emails.create! 
    end 

    def spam!(user) 
    begin 
     @user = user 
     Rails.logger.info("XYZZY: Sending spam!") 
     m = mail(to: user.email, subject: 'SPAM!') 
     Rails.logger.info("XYZZY: mail method finished") 
     m 
    rescue Errno::ECONNREFUSED 
     record_email 
    end 
    end 
end 
+0

che non funziona. Ha un po 'di come il ritardo del processo elabora l'e-mail. Chiama il tuo metodo mailer quindi invoca il recapito (o deliver_now) su di esso. Quindi la consegna effettiva viene eseguita al di fuori dello spam! metodo –

+0

Voglio anche qualcosa che funzioni senza dover ricordare di implementarlo su tutti i metodi mailer o chagne tutti i miei metodi mailer esistenti (ce ne sono circa 30) –

0

i messaggi di debug si mostravano perfettamente senso - l'azione mailer termina immediatamente perché la mailing azione stessa è asincrono, gestita da lavoro in ritardo in un processo completamente diverso. Quindi non c'è modo che la classe mailer stessa possa sapere come è finita l'azione di mailing.

Quello che penso che sia necessario invece è l'implementazione Delayed job hooks. Dovresti riscrivere i tuoi mailer e le chiamate per inviare e-mail un po 'però.

Non ho provato fino in fondo, ma qualcosa lungo le seguenti linee dovrebbe funzionare:

class MailerJob 

    def initialize(mailer_class, mailer_action, recipient, *params) 
    @mailer_class = mailer_class 
    @mailer_action = mailer_action 
    @recipient = recipient 
    @params = params 
    end 

    def perform 
    @mailer_class.send(@mailer_action, @recipient, *@params) 
    end 

    def success(job) 
    Rails.logger.debug "recording email!" 
    @recipient.emails.create! 
    end 

    def failure(job) 
    Rails.logger.debug "sending email to #{@recipient.email} failed!" 
    end 

end 

MailerJob è un custom job essere gestito da lavoro ritardata.Ho cercato di renderlo il più generale possibile, quindi accetta la classe mailer, l'azione mailer, il destinatario (tipicamente l'utente) e altri parametri facoltativi. Inoltre, è necessario che l'recipient abbia l'associazione emails.

Il lavoro ha due ganci definiti: success quando l'azione di mailing ha esito positivo che crea il record email nel database e un altro per l'errore di registrazione. L'invio effettivo viene eseguito nel metodo perform. Notare che al suo interno il metodo delayed non viene utilizzato poiché l'intero processo è già stato accodato in una coda di lavoro Ritardata in background quando viene richiamato.

Per inviare una mail utilizzando questo lavoro personalizzato è necessario enqueue al lavoro in ritardo, ad es .:

Delayed::Job.enqueue MailerJob.new(UserMailer, :spam!, User.find(1)) 
+0

Il mio collega l'ha suggerito. Penso che sia una buona idea, ma estenderei la funzionalità di Delayed :: PerformableMailer (https://github.com/collectiveidea/delayed_job/blob/v4.1.1/lib/delayed/performable_mailer.rb). Penso che tu stia sbagliando sul fatto che l'azione di mailing sia asincrona. Hai ragione, da dove chiamo UserMailer.delay.spam! il metodo mail avviene più tardi, ma il lavoro effettivo di invio dell'email è sincrono entro il lavoro ritardato. –

+0

Intendevo "asincrono" nel senso che l'azione del mailer non attende il risultato del mailing effettivo (fatto dal lavoro ritardato). E mi dispiace, ma non capisco quale problema hai con l'approccio degli ami? Dalla [fonte] (https://github.com/collectiveidea/delayed_job/blob/2014009496dd0b2af217ab322f74f91fe4e26097/lib/delayed/message_sending.rb#L2) sembra che internamente il PerformableMailer non sia altro che una classe che accoda i lavori di mailing piuttosto in allo stesso modo della mia risposta sopra. – BoraMa

+1

Perché il downvote? – BoraMa