Rails rails 是如何实现 before_filter 的?

hxhxhx88 · August 29, 2012 · Last by debugger replied at April 01, 2014 · 5519 hits

我想自己实现一下 rails 的 before_filter 功能,但是遇到了一个问题:ruby 要如何捕捉到一个函数的调用?

我的方法是利用 alias_method,但总感觉不太满意。

于是我去看 rails 的源代码。

注意到它是维护了一个 filter_chain,然后在目标函数调用之前调用设定好的 filter。

但是还是没有解决我的疑问:rails 是如何捕捉到函数的调用的呢?

具体地说,

比如,我又一个 class Dog,对其 bark 方法设定了一个 before_filter。那要想执行这个 filter,肯定要在我调用 dog.bark 时捕捉到这个调用。然后才能进行操作。

谁知道 rails 是怎么实现的吗。。。。

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/callbacks.rb 这个。 大致的实现方法就是所有的 callback 方法都会跑到 callback 模块里面的 run_callback() 这个函数里面去。 然后变成例如 run_callback(:destroy) ,然后根据传进来的参数会进行一个判断,区别是内联 block 还是一个 method 还是一个 symbol,然后调用 destroy_with_callbacks。 然后调用具体函数。 filter_chain 的底层应该是一个 alias_method_chain()

实现捕捉函数调用的原理大概就是这样:

def destroy puts "I have destroy something" end

def destroy_with_callbacks puts "i got call back" destroy()
end

alias_method :destroy_without_callback, :destroy alias_method :destroy, :destroy_with_callbacks

然后你运行 destroy 实际上就是运行了 destroy_with_callbacks 输出 i got call back I have destroy something 运行 destroy_without_callback 才是最初的 destroy 这样就实现了你说的捕捉。 写的比较乱,不知道能不能看懂。大概就是这样意思。

我也是 rails 新手,但是 c++ 的 cgi 我倒自己实现过 filter,简单来说就是装饰模式,楼主可以 google 一下 front controller。

#1 楼 @tassandar

嗯。我自己也是相当用两次 alias_method 来实现。

但是。我没有在源代码里找到 alias_method_chain 的部分。。能告诉我在哪里吗?

再 Rails 里面,alias_method_chain 已经被认为是一个不好的模式,对于楼主的问题,可以使用 AcitveSupport::Callback 实现,具体代码如下:

require 'active_support/call'
class Dog
  include  ActiveSupport::Callbacks

  define_callbacks :bark 
  def bark
    run_callbacks :bark do
      puts "wang wang "
    end
  end

  set_callback :bark, :after, :after_bark
  set_callback :bark, :before, :before_bark1
  set_callback :bark, :before, :before_bark2
end

rails 的 filter 是在 ActiveSupport::Callbacks 上封装了一层,具体可以看 actionpack/lib/abstract_controller/callbacks.rb

从 rails3 开始 alias_method_chain 已经被下面的方法所代替。


class Controller
  module BaseMethod
    def method_name
      # basic code
      p "A"
    end
  end

  module BeforMethod
    def method_name
      # some code befor
      p "B"
      super
    end
  end

  module AfterMethod
    def method_name
      super
      # some code after
      p "c"
    end
  end

  include BaseMethod
  include BeforMethod
  include AfterMethod
end


define_callbacks define_model_callbacks

You need to Sign in before reply, if you don't have an account, please Sign up first.