rails如何实现before_filter?

我对rails如何实现像before_filter这样的filter感兴趣。

但在阅读完源代码后,我仍然感到困惑。

我注意到rails的框架维护了filter_chain ,并在目标方法之前运行filter。

但是,我不明白一个重要的过程:rails如何捕获一个方法调用?

我的意思是,例如,我有一个类Dog ,并为方法树皮设置了before_filter

当我调用dog.bark ,rails应该以某种方式捕获此调用,并改为运行其修改后的方法。

但是,我在源代码中找不到这样的代码。

任何人都可以告诉我这个想法或指出代码所在的位置吗?

当您设置before_filter或任何类似的filter(想想after_filteraround_filter )时 ,您使用的是Symbol或Proc ,lambda或块。

 before_filter :bark before_filter Proc.new { |k| k.bark } 

上面通过调用set_callback将符号或块附加到堆栈。 这构建了您所指的“链”。

此“链”中的每个项都是ActiveSupport::Callbacks::Callback类的实例。 这堂课知道

  • 它必须运行的方法(符号)或块(即你的类’ :bark方法)
  • 它必须在其中运行的上下文(即你的Dog类)

Callback实例附加到ActiveSupport::Callbacks::CallbackChain

初始化每个Callback类时,运行_compile_filter以将filter从SymbolProc ,lambda或块规范化为通用的可调用格式。

最后,当运行CallbackChain时,它将在每个Callback实例上调用start此时它将在适当的上下文中实际执行filter。


重要的是要指出你不会创建像这样的filter

 before_filter dog.bark 

这将执行dog.bark并将其返回值传递给before_filter以附加到CallbackChain 。 目的是将一些指令传递给before_filter for Rails以便稍后执行。 你会做类似的事情

 before_filter Proc.new { d = Dog.new; d.bark } 

Proc的代码未执行。 当上面的行由Rails运行时。 相反,Rails被告知将Proc传递给CallbackChainProc是您传递给Rails以在适当时间执行的“指令”。


铁轨知道我怎么称呼:树皮

至于这个,让我们说你的Dog类被简单地定义为

 class Dog def bark end def eat end end 

(虽然这是一个可怕的例子) ,你可能想要有类似的东西

 before_bark :eat 

这需要你定义bark回调,然后告诉你的bark方法运行相关的bark回调。

 class Dog extend ActiveModel::Callbacks define_callbacks :bark before_bark :eat def bark run_callbacks(:bark) { YOUR BARK CODE HERE } end def eat end end 

您可以看到ActiveRecord::Callbacks如何做到这一点。

这真的是一个不好的例子,因为你可以(而且应该)直接从bark直接打电话eat ,但这应该得到重点。

Rails不会按照您描述的方式捕获方法调用。 如果查看AbstractController::Base.process ,它将查找要为调度操作调用的方法,运行filter然后调用实际方法。 换句话说,您的控制器方法不是直接调用,而是通过此process方法调用。