检测after_action回调中的操作邮件传递失败

时间:2016-03-21 20:03:11

标签: ruby-on-rails ruby-on-rails-4 actionmailer delayed-job

我在邮件程序中使用after_action回调来记录发送的电子邮件。电子邮件通过延迟作业发送。这是有效的,除非我们无法访问远程服务器 - 在这种情况下,电子邮件不会发送,但我们记录它是。延迟作业稍后重试该电子邮件,并且已成功发送,但我们已记录了已发送的两封电子邮件。

它看起来像这样:

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

我这样称呼这个代码(使用delayed job performable mailer):

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

当我在调试器中逐步执行此操作时,似乎在传递邮件之前调用了我的after_action方法。

[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

如何在我的邮件程序方法中捕获网络错误并记录电子邮件尝试失败,或者什么都不做?我正在使用Rails 4.2.4。

3 个答案:

答案 0 :(得分:2)

这就是我想出的,我希望有更好的方法。

我使用了邮件传递回调:

<强> 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

<强>配置/初始化/ mail.rb

Mail.register_observer( DeliveryCallback.new )

我替换了我的record_email方法:

class UserMailer < ActionMailer::Base

  after_action :record_email

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

这似乎有效,如果远程服务器不可用,则不会调用deliver_email回调。

有更好的方法!?!?

答案 1 :(得分:0)

您显示的调试消息非常有意义 - 邮件程序操作立即完成,因为邮件操作本身是异步,由Delayed作业在完全不同的过程中处理。因此,邮件程序类本身无法知道邮件操作是如何完成的。

我认为您需要的是实施 Delayed job hooks 。你必须重写你的邮件和调用电子邮件。

我还没有完全测试过,但以下几行应该有效:

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是由Delayed作业运行的custom job。我尽量使它尽可能通用,因此它接受邮件程序类,邮件程序操作,收件人(通常是用户)和其他可选参数。此外,它还要求recipient具有emails关联。

该作业定义了两个挂钩:success当邮件操作成功时,会在数据库中创建email记录,另一个挂起则记录失败。实际发送是在perform方法中完成的。请注意,在其中不使用delayed方法,因为整个作业在被调用时已经在后台延迟作业队列中排队。

要使用此自定义作业发送邮件,您必须将其排入延迟作业,例如:

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

答案 2 :(得分:-1)

尝试以下方法:

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