我正在尝试在我的应用程序中实现审计跟踪,但由于某些要求,我无法使用任何现有的宝石或插件。
我想转移任何将模型更新为自定义方法的正常尝试,该方法将所有更新保存在另一个单独的表中(称为更新)。
然后,应用程序使用更新表来实际执行更新。
现在我已经重载了create_or_update以获得功能的第一部分
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : create_updates
result != false
end
class Update < ActiveRecord::Base
belongs_to :updatable, :polymorphic => true
after_create :update_model
private
def update_model
self.updatable.update_attribute self.attribute, self.new_value #infinite loop
end
end
现在的问题是,当更新模型尝试实际执行更新时,这会导致无限循环。
我一直在查看rails核心源,找到绕过第一个功能的最佳位置。我希望在事务内部执行这些更新,但我不确定在活动记录堆栈中的确切位置是从哪里开始还是结束。我也不想开始攻击活跃的资源。
任何建议都将不胜感激。
答案 0 :(得分:1)
您是否确实需要将属性保存在单独的表中,然后在管理员查看并批准后执行更新?如果是这种情况,您可能只想覆盖更新方法以执行以下操作:
def update(perform_updates = false)
if perform_updates
latest_approved_update = UpdateAuditor.first(:conditions => { :updatable_id => self.id, :updatable_type => self.class.name, :approved => true })
self.attributes = latest_approved_update.attributes
self.save
else
UpdateAuditor.create(:updatable_id => self.id, :updatable_type => self.class.name, :attributes => self.attributes)
end
end
更新:作者评论说他们希望能够将此模型应用于所有更新。为了实现这一点,你可以在模型中添加一个attr_accessor,比如说“perform_updates”,默认情况下这肯定是零。
如果要对数据库执行更新,首先必须将该属性设置为true,然后运行update。否则,更新将只创建一个新的UpdateAuditor记录,需要得到管理员的批准。
class Person < ActiveRecord::Base
has_many :audits, :class_name => "UpdateAudit", :as => :auditable
attr_accessor :perform_updates
private
def create_or_update
raise ReadOnlyRecord if readonly?
if new_record?
result = create
result != false
else
if perform_updates
latest_approved_update = audits.approved.last
if latest_approved_update
self.attributes = latest_approved_update.attributes
update
else
return false
end
else
audits.create(:updated_attributes => self.attributes)
end
end
end
end
为了记录,我认为覆盖默认更新方法是一个危险的游戏,并且这种编程在它所属的before_update
回调中更好。一旦在某个界面中批准了更新,则观察者可以执行更新,覆盖当前存在的更新,直到可以批准所做的另一个更改。如果队列中的对象当前有更新要批准,则可以提醒用户正在等待批准的更改等。