假设我们有以下场景:
class MyModel < ActiveRecord::Base
after_save :throw_after_save
after_commit :throw_after_commit
private
def throw_after_save
raise "raising on after_save"
end
def throw_after_commit
raise "raising on after_commit"
end
end
class MyController < ApplicationController
def callback
begin
MyModel.new(params).save
rescue
flash[:alert] = "Failed persisting to external system. Try again."
Airbrake.notify(
error_class: "External System Persistence",
error_message: "External System Persistence: Failed to persist data",
parameters: params
)
end
redirect_to root_path
end
end
我们从外部系统获得回调(用户填写一些数据并为帐户创建一组临时属性)。
让我们假设我们希望在对我们进行回调之后在本地保存一些数据。在这个数据被持久化之后,我们想要调用外部系统来完成账户创建过程。外部系统将返回一个结果,通知我们成功,其中包含一些我们需要在本地持久保存的额外数据。我们还知道,在某些特殊情况下,远程系统上的持久性不会成功(假设系统不可用,或者他们的一端出了问题)。
目标是捕获外部持久性异常以及成功并采取相应措施。在成功的情况下,一切都是笨拙的:额外的数据存储在本地,redirect_to root_path
发生了。然而,在异常的情况下,我们希望向用户表明这一点(也许设置 aflash[:alert]
显示在视图中)。
我们曾尝试使用ActiveRecord::Callbacks
从模型中抛出异常,after_save
并after_commit
通过设置警报并可能将异常传递给某些异常通知系统(如 Airbrake)在控制器处处理该异常。在 的情况下after_save
,模型抛出异常并被控制器捕获,但不保存记录(并且我们要求即使在外部系统出现异常的情况下也要存储部分数据 - 这是不可接受的)。在 的情况下after_commit
,异常不会被控制器抛出和拾取,而是部分记录被持久化。这意味着我们无法将异常通知用户(除非我们实现了一些通知推送机制——这太过分了)。
事实证明,我们可以在模型上设置错误after_save
,这很好。但是,这是处理这种情况的一个很好的通用模式吗?