2012-08-29 69 views
18

我對軌道如何實現像before_filter這樣的過濾器感興趣。rails如何實現before_filter?

但在閱讀源代碼後,我仍然感到困惑。

我注意到rails的框架維護着filter_chain,並且在目標方法之前運行這些過濾器。

但是,我不明白一個重要的過程:軌道如何捕獲一個方法調用?

我的意思是,例如,我有一個類Dog,並設置一個before_filter的方法樹皮。

當我打電話給dog.bark時,ra​​ils應以某種方式捕獲此調用,並運行其修改方法。

但是,我沒有在源代碼中找到這樣的代碼。

任何人都可以告訴我這個想法或指出代碼在哪裏?

回答

20

當您設置一個before_filter,或任何類似的過濾器(認爲after_filteraround_filter,你與無論是SymbolProc,拉姆達或阻止這樣做。

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

上述追加符號或塊的堆疊here,通過調用set_callback。這會建立你所指的'連鎖'。

此'鏈'中的每個項目都是ActiveSupport::Callbacks::Callback類的實例。該類知道

  • 的方法(符號)或阻止它必須運行(即你的類:bark法)
  • 它必須在運行(即你的Dog類)上下文

Callback實例附加到的__update_callbacks中。

當每個Callback類初始化,_compile_filter運行以從SymbolProc,λ-正常化過濾器,或阻止到一個共同的,可調用的格式。

最後,在運行時CallbackChain,它會調用start每個Callback實例,其在這一點上,過濾器實際上是正確的上下文中執行。


重要的是要指出的是,你會創建一個過濾器像

before_filter dog.bark 

這是要執行dog.bark,並通過它的返回值before_filter被附加到CallbackChain是很重要的。其目的是爲了讓Rails稍後爲您執行某些指令而將其指令傳遞給before_filter。你會改爲像

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

Proc內的代碼不會執行。當上面的行由Rails運行時。相反,Rails被告知通過ProcCallbackChainProc是您傳遞給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,但這應該得到重點。

+0

謝謝你!但仍然困惑。你說'最後,當CallbackChain運行...'時,我的問題是軌道如何知道應該運行CallbackChain?說,如果我設置了before_filter:吃課堂狗。然後,當我叫dog.bark時,應該先吃東西。但是,軌道如何知道我所稱的:樹皮? – HanXu

+0

我更新了我的答案。 – deefour

+0

更清晰,但仍有點困惑。在你的例子中,我們手動重新定義了樹皮,但是rails通過一些代碼自動完成。我不知道這些代碼在哪裏。根據@harald的說法,rails已經提前修改了每個動作。我注意到AbstractController :: Base中的'attr_internal',有沒有魔法發生? – HanXu

1

Rails不會按照您描述的方式捕獲方法調用。如果你看看AbstractController::Base.process,它會查找調用動作的方法,運行過濾器然後調用實際的方法。換句話說,你的控制器方法不是直接調用的,而是通過這個方法來調用。

+0

我明白了,rails似乎使用'attr_internal'將每個方法都改爲新的內部名稱,並用新名稱做所有事情? – HanXu