2016-03-21 14 views
7

Ich verwende einen Rückruf after_action in meine Mailer, um aufzuzeichnen, dass E-Mail gesendet wurde. Die E-Mails werden über einen verzögerten Job gesendet. Dies funktioniert, außer wenn wir den Remote-Server nicht erreichen können. In diesem Fall wird die E-Mail nicht gesendet, aber wir registrieren, dass dies der Fall war. Verzögerter Job wiederholt die E-Mail später und wird erfolgreich zugestellt, aber wir haben dann aufgezeichnet, dass zwei E-Mails gesendet wurden.Erkennen von Aktion Mailer Delivery-Fehler in AfterAction-Callbacks

Es sieht ungefähr so ​​aus:

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 

ich diesen Code wie folgt aufrufen (mit delayed job performable mailer):

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

Wenn ich durch diese in einem Debugger Schritt, wie es scheint, dass meine after_action Methode aufgerufen wird bevor die Mail zugestellt wird

[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 

Wie kann ich Netzwerkfehler in meinem Mailer-Methoden und notieren, dass der E-Mail-Versuch fehlgeschlagen ist, oder gar nichts tun? Ich benutze Rails 4.2.4.

Antwort

2

Das ist, was ich kam mit, ich würde gerne einen besseren Weg haben.

verwendete ich die Postzustellung Rückruf:

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/initialisiert/mail.rb

Mail.register_observer(DeliveryCallback.new) 

Und ich meine record_email Methode ersetzt:

class UserMailer < ActionMailer::Base 

    after_action :record_email 

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

Dies scheint zu funktionieren, wenn der Remote-Server nicht verfügbar ist, wird der Callback von delivered_email nicht aufgerufen.

Gibt es einen besseren Weg!?!?

-1

Versuchen Sie Folgendes:

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

das nicht funktioniert haben zu schicken. Es hat ein bisschen mit wie verzögerter Job verarbeitet die E-Mail. Er ruft Ihre Mailer-Methode auf und ruft dann die Zustellung (oder deliver_now) darauf auf. Die eigentliche Zustellung erfolgt also außerhalb des Spams! Methode –

+0

Ich möchte auch etwas, das funktioniert, ohne daran zu erinnern, es bei jeder Mailer-Methode zu implementieren oder chagne alle meine bestehenden Mailer-Methoden (es gibt etwa 30) –

0

Die Debug-Meldungen zeigen Sie durchaus Sinn machen - die Mailer Aktion endet sofort, weil die Post Aktion selbst asynchron ist, durch Delayed Job in einem völlig anderen Prozess behandelt. Daher kann die Mailerklasse selbst nicht wissen, wie die Mailing-Aktion beendet wurde.

Was ich denke, dass Sie stattdessen benötigen, ist die Implementierung Delayed job hooks. Sie müssten Ihre Mailer und die Anrufe, um E-Mails ein wenig zu schreiben, neu schreiben.

Ich habe es nicht vollständig getestet, aber etwas in den folgenden Zeilen funktionieren soll:

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 ein custom job ist von Delayed Job ausgeführt werden.Ich habe versucht, es so allgemein wie möglich zu machen, also akzeptiert es die Mailerklasse, die Maileraktion, den Empfänger (typischerweise den Benutzer) und andere optionale Parameter. Es erfordert auch die recipient, die emails Zuordnung zu haben.

Der Job hat zwei definierte Hooks: success, wenn die Mailing-Aktion erfolgreich ist, die den Datensatz email in der Datenbank erstellt und einen weiteren für Protokollierungsfehler. Das eigentliche Senden erfolgt in der perform Methode. Beachten Sie, dass darin die -Methode nicht verwendet wird, da der gesamte Job bereits in einer Warteschlange mit verzögerter Job-Warteschlange eingereiht ist, wenn er aufgerufen wird.

Um eine E-Mail mit diesem kundenspezifischen Auftrag, den Sie enqueue es zu verzögerten Job, z.B .:

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

Mein Kollege schlug vor. Ich denke, es ist eine ziemlich gute Idee, aber ich würde die Funktionalität von Delayed :: PerformableMailer (https://github.com/collecticidea/delayed_job/blob/v4.1.1/lib/delayed/performable_mailer.rb) erweitern. Ich denke, Sie irren sich, weil die Mailing-Aktion asynchron ist. Du hast recht, von wo ich UserMailer.delay.spam nenne! Die E-Mail-Methode wird später ausgeführt, aber die eigentliche Aufgabe, die E-Mail zu senden, ist innerhalb eines verzögerten Auftrags synchron. –

+0

Ich meinte "asynchron" in dem Sinne, dass die Mailer-Aktion nicht auf das Ergebnis des eigentlichen Mailings wartet (erledigt durch einen verzögerten Job). Und es tut mir leid, aber ich verstehe nicht ganz, welches Problem Sie mit dem Haken Ansatz haben? Von der [Quelle] (https://github.com/collectiveidea/delayed_job/blob/2014009496dd0b2af217ab322f74f91fe4e26097/lib/delayed/message_sending.rb#L2) scheint es, dass intern der PerformableMailer nichts anderes als eine Klasse ist, die Mailing-Jobs ziemlich in die Warteschlange stellt genauso wie meine Antwort oben. – BoraMa

+1

Warum der Downvote? – BoraMa