Callbacks 底层简要分析
ActiveSupport::Callbacks 本身可分为几部分。
Callback Chain 回调链
define_callbacks 主要作用就是定义一条"回调链",每一条链都是 Callback Chain 的实例对象。
Callback Chain 实例对象,大致如下:
#<ActiveSupport::Callbacks::CallbackChain:0x007fc6ebf70648
@callbacks=nil,
@chain=[],
@config={:scope=>[:kind]},
@mutex=#<Mutex:0x007fc6ebf70580>,
@name=:checkout>其中,最重要的信息有:
@chain此回调链所包含的"所有回调"。@name此回调链的"名字"。
Rails 没有提供查看所有 callback chain 信息的接口,只能间接查看。
比如:
# 所有"回调链"
callback_chains = ObjectSpace.each_object(ActiveSupport::Callbacks::CallbackChain)
# 不为空的"回调链"
chains = callback_chains.select{|cc| cc.instance_variable_get("@chain").present? }
# 所有"回调链"的名字
callback_chains_uniq_name = chains.map(&:name).uniq但对于某个类而言,则很方便。如:
Callback 回调
上面提到每一条回调链的 @chain 里包含了它"所有的回调",这里的每一个"回调"就是一个 Callback 实例对象。
Callback 实例对象,大致如下:
其中,最重要的信息有:
@if或@unless此回调起作用的"前提条件"。@kind此回调的"回调类型"。(对于 Rails 而言,可以选择 :before, :after, :around 其中之一)@name此回调所加入的"回调链的名字"。@key此回调的"名字"。(如果没有名字则用 object_id 代替)@filter此回调"真正要执行的代码"。(一般只显示名字,也就是说和 @key 一样;如果没有名字,则显示定义它时的文件及行号)
"真正要执行的代码",可以是以下类型:
这些不同类型的"真正要执行的代码",之后都会被 Callback 的 make_lambda 方法转换成 lambda 对象,再然后处理过程类似。
其它
当 @filter 是一个实例对象,并且恰好有和"回调类型"相同的方法
上面也提到"回调类型",Rails 默认有 :before、:after 和 :around. 上面提到了回调里"真正要执行的代码"可以是一个实例对象,当触发回调时,会执行它的同名"回调"方法。
如果 @filter 恰好是一个实例对象,而这个实例对象又恰好有 :before、:after 或 :around,则此时会出现和想像中不一样的结果。
比如:
此时,我们可以使用 define_callbacks 的 scope 参数进行解决。也就是:
Rails 里 Callback 相关的模块及继承关系
Filters 顺序
一条"回调链"上可以有多个"回调",它们彼此之间不是独立的,有先后顺序。即:
Before,After,Around
但这些"回调"也有终结的时候。即:
End
定义、运行回调
define_callbacks 定义的时候会:
run_callbacks 运行的时候会:
最后更新于
这有帮助吗?