创建记录后,我发送了一封电子邮件,我在after_commit
回调中发送了这封电子邮件。我想将电子邮件的Message-Id
标题保存为记录中的属性,以便稍后使用。我将其实现为:
after_commit on: :create do
if email = Mailer.email(self).deliver
# `self.message_id = email.message_id` has no effect, so I'm calling update()
self.update message_id: email.message_id
end
end
令人惊讶(对我而言),这会导致无限的电子邮件发送循环(抱歉,Mailgun);看来即使指定了on: :create
,也会在更新时调用回调。
我没有看到这种方法有什么问题吗?我怎么能把这个值附加到这个记录上?
我唯一的想法是尝试gating the callback on previous_changes
,但无论如何我想了解为什么这不能正常工作。
答案 0 :(得分:3)
我还希望update
回调中的after_commit
仅触发on: update
回调。但是我在Rails源代码中找到了this:
def committed!(should_run_callbacks = true) #:nodoc:
_run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
ensure
force_clear_transaction_record_state
end
只有在运行所有after_commit回调之后,Rails才会清除事务的new_record状态。
self.update_columns message_id: email.message_id
这不会创建事务,因此不会触发另一个after_commit回调。我认为在交易记录外发送电子邮件和商店ID是合适的。毕竟,如果出现故障,您无法使用消息ID 和回滚发送电子邮件来回滚更新记录。它本质上不是原子的。
在after_commit
回调队列中,发送带有消息ID的电子邮件和更新记录的ActiveJob作业。使用像SuckerPunch或DelayedJob这样的真实后端,作业将以记录“Global ID”排队。 Job的perform
方法获取一个新加载的记录,其实例变量中没有存储事务状态。
答案 1 :(得分:0)
这可能有效
after_commit :email_send, :if => lambda{ new_record? }
或
after_commit :email_send, :on => :create
或
after_create :email_send
def email_send
if email = Mailer.email(self).deliver
# `self.message_id = email.message_id` has no effect, so I'm calling update()
self.update message_id: email.message_id
end
end
答案 2 :(得分:0)
检查message_id
的先前存在似乎已在我的特定情况下解决了问题:
after_commit on: :create do
unless self.message_id
if email = Mailer.email(self).deliver
self.update message_id: email.message_id
end
end
end
但是我想可能有一种情况我无法逃避,所以理解为什么在更新时调用on: :create
回调仍然很好。