2016-12-09 38 views
1

所以,這裏有一個有趣的問題,我查看elixir中的模塊屬性文檔,即底部的http://elixir-lang.org/getting-started/module-attributes.html,它提到它們可以用作ExUnit中的方法註釋。使用模塊註釋作爲方法屬性

不幸的是,基本上沒有關於如何實現這一點的信息,並且查看ExUnit代碼讓我迷失了方向。看起來我需要確定最接近該屬性的方法,說它們以某種方式關聯(儘管可能是錯誤的)。

任何想法,我可能會了解這一點?

+1

你見過這個頁面嗎? http://elixir-lang.org/docs/master/ex_unit/ExUnit.Case.html更具體地說,該頁面的這一部分似乎與您的問題相關:http://elixir-lang.org/docs/master/ex_unit/ ExUnit.Case.html#module-tags在這個頁面上有什麼東西不清楚嗎?你的問題對我來說不是很清楚。 –

+0

我不知道你的表現,我是的ExUnit有@tags作爲功能的註釋,我的問題是他們如何實現這一點。我實際上解決了這個問題,現在我又遇到了另一個問題,即在編譯時能夠對這些註釋做些有用的事情。在這裏我的付出:https://github.com/chrisjowen/annotatable – Owen

+0

好的,然後把我的評論作爲一個跡象,它不是非常清楚你想知道什麼。 –

回答

2

它的工作原理是這樣的。看看ExUnit.Case的源代碼。

首先,查看__using__宏,因爲它將在測試用例中使用時首先被調用。特別要注意here

Enum.each [:ex_unit_tests, :tag, :describetag, :moduletag, :ex_unit_registered], 
     &Module.register_attribute(__MODULE__, &1, accumulate: true) 

這將註冊@tag和一堆多個屬性的積累。閱讀Module.register_attribute/3的文檔,你會看到它意味着隨時調用屬性,該值會附加到以前的屬性列表。

然後記test/3宏,特別here

quote bind_quoted: [var: var, contents: contents, message: message] do 
    name = ExUnit.Case.register_test(__ENV__, :test, message, []) 
    def unquote(name)(unquote(var)), do: unquote(contents) 
end 

注意調用ExUnit.Case.register_test/4。看着它,特別是here

tag = Module.delete_attribute(mod, :tag) 

它獲取的標籤,直到這裏,並刪除它們。並且通過具有標籤,並且測試的名稱,它調用(here

test = %ExUnit.Test{name: name, case: mod, tags: tags} 
Module.put_attribute(mod, :ex_unit_tests, test) 

從而節省了與另一個屬性標籤內沿測試。

,最後,請注意這裏

@doc false 
    defmacro __before_compile__(_) do 
    quote do 
     def __ex_unit__(:case) do 
     %ExUnit.TestCase{name: __MODULE__, tests: @ex_unit_tests} 
     end 
    end 
    end 

功能__ex_unit__/1被稱爲ExUnit.Runner.run_case/3獲得的每一種情況下的內部測試的信息。

你明白了嗎?使用累積屬性,在你的宏內部調用一個函數,該函數總是得到屬性的當前值並清除它,然後對這個值做任何你想要的事情,因爲你知道調用宏時它總是這樣。

我希望已經足夠清楚了,如果您需要更多解釋,請留下評論。

PS。我只是閱讀源代碼來找出這一點。知道它的工作原理令人興奮。

+0

我接受答案,因爲這是非常重要的。我實際上已經想出了這一點,並打開了這個小庫來註釋任何方法。正如你所說的,重要的部分是accumulate:register_attribute的標籤。它的相當不錯,elixir並不真的支持方法註釋,但是這種語言已經足夠讓你自己去做:) – Owen

+1

哦,如果其他人對此感興趣,還有一件事需要提及。 ExUnit要求調用測試宏,它依次檢索並刪除之前定義的屬性。如果我們正在定義一個方法,那麼將不會調用我們可以插入的宏,但幸運的是有一個模塊生命週期方法,我們可以使用@on_defenition(請參閱https://hexdocs.pm/elixir/Module.html) – Owen