21

我对 rails 如何实现过滤器感兴趣before_filter

但是看了源码后,我还是一头雾水。

我注意到 rails 的框架维护一个filter_chain, 并在目标方法之前运行过滤器。

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

我的意思是,例如,我有一个 class Dog,并将 a 设置before_filter为方法 bark。

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

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

谁能告诉我这个想法或指出代码在哪里?

4

2 回答 2

22

当您设置 abefore_filter或任何类似的过滤器(想想after_filter, around_filter时,您可以使用SymbolProc、 lambda 或块。

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

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

此“链”中的每个项目都是该类的一个实例ActiveSupport::Callbacks::Callback。这个班知道

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

Callback实例附加到ActiveSupport::Callbacks::CallbackChainin 。__update_callbacks

当每个Callback类初始化时,_compile_filter运行以将过滤器从SymbolProc、 lambda 或块标准化为通用的可调用格式。

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


重要的是要指出您不会创建像这样的过滤器

before_filter dog.bark

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

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

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


首先,rails 是如何知道我打过电话的:bark

至于这个,假设你的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做到的。

这确实是一个不好的例子,因为您可以(并且应该)eat直接从 调用bark,但这应该明白这一点。

于 2012-08-29T11:08:03.223 回答
1

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

于 2012-08-29T11:15:25.653 回答