【问题标题】:How does rails implement before_filter?rails 是如何实现 before_filter 的?
【发布时间】:2012-08-29 09:25:42
【问题描述】:

我对 Rails 如何实现 before_filter 之类的过滤器感兴趣。

但是看了源码还是一头雾水

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

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

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

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

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

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

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    当您设置before_filter 或任何类似的过滤器(想想after_filteraround_filter,您可以使用SymbolProc, lambda 或块。

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

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

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

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

    Callback 实例附加到__update_callbacks 中的ActiveSupport::Callbacks::CallbackChain

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

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


    请务必指出,您将不会创建类似

    的过滤器
    before_filter dog.bark
    

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

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

    Proc 中的代码未执行。当上面的行由 Rails 运行时。相反,Rails 被告知将Proc 传递给CallbackChainProc 是您传递给 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 是如何做到这一点的。

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

    【讨论】:

    • 首先谢谢您!但是还是很迷茫。你说'最后,当 CallbackChain 运行时......',我的问题是 rails 是如何知道 CallbackChain 应该运行的?比如说,如果我将 before_filter :eat 设置为 Dog 类。然后当我调用 dog.bark 时, :eat 应该首先运行。但是,rails 是如何知道我调用了 :bark 的?
    • 清晰多了,但还是有点困惑。在您的示例中,我们手动重新定义了 bark,但 rails 会通过一些代码自动执行此操作。我找不到这些代码所在的位置。根据@harald 的说法,rails 已经提前修改了每个动作。我注意到 AbstractController::Base 中的“attr_internal”,是否发生了魔法?
    • 对于扩展 ActionController::Base 的类,run_callbackshere
    【解决方案2】:

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

    【讨论】:

    • 我明白了,rails 似乎使用 'attr_internal' 将每个方法更改为新的内部名称并使用新名称执行所有操作?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 2013-05-07
    相关资源
    最近更新 更多