我正在使用Ancestry宝石。每个节点都有一个与其关联的数值(:value)。每次对一个节点进行CRUD时,我都希望重新计算整个树,以便每个节点最终得到其子节点的值的总和。(叶节点保持不变。)
我的控制器操作包含以下代码以触发重新计算:
def create
...
@tree = Tree.new(tree_params)
if @tree.save
Tree.roots.each do |r|
recalc_tree(r)
end
redirect_to trees_url, notice: "Node was successfully created. " + note.to_s
else
render :new, notice: "There was an error saving this node. Please try again."
end
在控制器底部的私人部分,我有一个自定义的助手方法:
def recalc_tree(r)
total = 0
if r.has_children?
r.children.each do |c|
total = total + recalc_tree(c)
end
r.value = total
else
r.value
end
end
我在浏览器中没有收到任何错误(节点保存正确),但这也不会更新祖先节点的任何值。
我试着在控制台中运行助手,但出现了一个"未定义的方法"错误:
>> helper.recalc_tree(Tree.roots.first)
Tree Load (0.0ms) SELECT "trees".* FROM "trees" WHERE "trees"."ancestry" IS NULL ORDER BY "trees"."id" ASC LIMIT 1
NoMethodError: undefined method `recalc_tree' for #<ActionView::Base:0x5fc82c0>
我做错了什么?
实现这一点的更多rails风格的方法是使用ActiveRecord的回调(http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html)。此外,由于当更新节点时,只有更新节点的祖先需要更新,因此我们可以通过只更新根路径上的祖先来节省一些CPU周期。
示例:
class Tree < ActiveRecord::Base
after_save :update_ancestors
...
...
private
def update_ancestors
self.ancestors.each do |ancestor|
ancestor.value = ancestor.descendants.pluck(:value).sum
ancestor.save
end
end
end
我实际上并没有在我的机器上运行这个代码,但这几乎是这个问题的方向!