檢視原始碼 撰寫文件

Elixir 將文件視為一級公民。文件必須容易撰寫且容易閱讀。在本指南中,您將學習如何使用 Elixir 撰寫文件,涵蓋模組屬性、樣式規範和 doctest 等結構。

Markdown

Elixir 文件使用 Markdown 撰寫。網路上有許多 Markdown 指南,我們建議從 GitHub 的指南開始

模組屬性

Elixir 中的文件通常附加到模組屬性。我們來看一個範例

defmodule MyApp.Hello do
  @moduledoc """
  This is the Hello module.
  """
  @moduledoc since: "1.0.0"

  @doc """
  Says hello to the given `name`.

  Returns `:ok`.

  ## Examples

      iex> MyApp.Hello.world(:john)
      :ok

  """
  @doc since: "1.3.0"
  def world(name) do
    IO.puts("hello #{name}")
  end
end

@moduledoc 屬性用於將文件新增到模組。@doc 用於函式之前,提供函式的文件。除了上述屬性之外,@typedoc 也可用於將文件附加到定義為類型規格一部分的類型,我們稍後將探討。Elixir 也允許將元資料附加到文件,方法是將關鍵字清單傳遞給 @doc 和相關屬性。

函式引數

在記錄函式時,引數名稱由編譯器推斷。例如

def size(%{size: size}) do
  size
end

編譯器會將此參數推論為 map。有時推論會次佳,特別是如果函數包含多個子句,而參數每次都與不同的值相符。您可以在實作前的任何時刻,僅宣告函數開頭,以指定適當的名稱供文件使用

def size(map_with_size)
def size(%{size: size}) do
  size
end

文件元資料

Elixir 允許開發人員將任意元資料附加到文件。這是透過將關鍵字清單傳遞給相關屬性(例如 @moduledoc@typedoc@doc)來完成的。常用的元資料是 :since,它會註解在範例中所示的特定模組、函數、類型或回呼新增的版本中。

另一個常見的元資料是 :deprecated,它會在文件中發出警告,說明不建議使用它

@doc deprecated: "Use Foo.bar/2 instead"

請注意,當開發人員呼叫函數時,:deprecated 鍵不會發出警告。如果您希望程式碼也發出警告,可以使用 @deprecated 屬性

@deprecated "Use Foo.bar/2 instead"

元資料可以有任何鍵。文件工具通常使用元資料為讀者提供更多資料並豐富使用者體驗。

建議

撰寫文件時

  • 保持文件的第一段簡潔明瞭,通常為一行。像 ExDoc 等工具使用第一行來產生摘要。

  • 以模組的全名來參照模組。

    Markdown 使用反引號 (`) 來引用程式碼。Elixir 建構在它上面,在參照模組或函數名稱時自動產生連結。因此,請務必使用完整的模組名稱。如果您有一個名為 MyApp.Hello 的模組,請務必將它參照為 `MyApp.Hello`,而不要參照為 `Hello`

  • 如果函數是本地的,請以名稱和元數來參照函數,例如 `world/1`,或者如果指向外部模組,請以模組、名稱和元數來參照:`MyApp.Hello.world/1`

  • @callback 前加上 c: 來參照 @callback,例如 `c:world/1`

  • @type 前加上 t: 來參照 @type,例如 `t:values/0`

  • 使用第二層 Markdown 標題 ## 來開始新的區段。第一層標題保留給模組和函數名稱。

  • 將文件放在多子句函數的第一個子句之前。文件總是針對每個函數和元數,而不是針對每個子句。

  • 在文件元資料中使用 :since 鍵,以註解 API 中新增任何新函式或模組的時間。

文件測試

我們建議開發人員在文件範例中加入範例,通常在自己的 ## Examples 標題下。為了確保範例不會過時,Elixir 的測試架構 (ExUnit) 提供一個名為文件測試的功能,讓開發人員可以測試文件中的範例。文件測試會從文件解析出以 iex> 開頭的程式碼範例。你可以在 ExUnit.DocTest 了解更多相關資訊。

文件 != 程式碼註解

Elixir 將文件和程式碼註解視為不同的概念。文件是您與應用程式介面 (API) 使用者之間的明確合約,無論是第三方開發人員、同事或未來的您。如果模組和函式是 API 的一部分,則必須始終記錄文件。

程式碼註解是針對閱讀程式碼的開發人員。它們可用於標記改進、留下筆記(例如,為什麼您必須因函式庫中的錯誤而使用解決方法),等等。它們與原始程式碼相關聯:您可以完全改寫函式並移除所有現有的程式碼註解,它將繼續以相同的方式運作,其行為或文件都不會改變。

由於無法從外部存取私有函式,因此如果私有函式具有 @doc 屬性,Elixir 會發出警告並捨棄其內容。但是,您可以像對任何其他程式碼一樣,將程式碼註解新增到私有函式,我們建議開發人員在認為這樣做會為此類程式碼的讀者和維護人員新增相關資訊時,這麼做。

總之,文件是與 API 使用者(他們不一定有權限存取原始碼)的合約,而程式碼註解則是提供給直接與原始碼互動的人員。透過區分這兩個概念,你可以了解並表達關於軟體的不同保證。

隱藏內部模組和函式

除了模組和函式庫提供為其公開介面的部分外,函式庫也可能實作並非其 API 一部分的重要功能。雖然可以存取這些模組和函式,但它們應為函式庫內部,因此不應提供給最終使用者的文件。

Elixir 允許開發人員透過設定 @doc false 來隱藏特定函式,或設定 @moduledoc false 來隱藏整個模組,這相當方便。如果隱藏一個模組,你甚至可以記錄模組中的函式,但模組本身不會列在文件中

defmodule MyApp.Hidden do
  @moduledoc false

  @doc """
  This function won't be listed in docs.
  """
  def function_that_wont_be_listed_in_docs do
    # ...
  end
end

如果你不想隱藏整個模組,你可以個別隱藏函式

defmodule MyApp.Sample do
  @doc false
  def add(a, b), do: a + b
end

不過,請記住 @moduledoc false@doc false 並不會讓函式變成私有的。上面的函式仍可以作為 MyApp.Sample.add(1, 2) 呼叫。不僅如此,如果匯入 MyApp.Sampleadd/2 函式也會匯入呼叫者。由於這些原因,在函式中加入 @doc false 時要小心,改用以下兩個選項之一

  • 將未記錄的函式移至具有 @moduledoc false 的模組,例如 MyApp.Hidden,確保函式不會意外公開或匯入。請記住,你可以使用 @moduledoc false 來隱藏整個模組,並仍使用 @doc 記錄每個函式。工具仍然會忽略該模組。

  • 函式名稱以一個或兩個底線開頭,例如 __add__/2。以底線開頭的函式會自動視為隱藏,不過你也可以明確加上 @doc false。編譯器不會匯入以底線開頭的函式,而且會暗示任何閱讀程式碼的人員其預期的私用用途。

Code.fetch_docs/1

Elixir 會將文件儲存在位元組碼中預先定義的區塊內。載入模組時並不會將文件載入至記憶體中,而是使用 Code.fetch_docs/1 函數從磁碟中的位元組碼中讀取。缺點是,在記憶體中定義的模組(例如在 IEx 中定義的模組)無法存取其文件,因為它們不會將其位元組碼寫入磁碟。