[Translation] Elixir Getting Started 14 模塊屬性

http://elixir-lang.org/getting_started/14.htmlhtml

在Elixir中模塊屬性有三個目的:git

  • 它們用於模塊註釋,用戶或VM的可以使用這些信息。
  • 相似常量的功能
  • 在編譯時做爲臨時的模塊存儲(module storage)

14.1 做爲註釋(As annotations)

Elixir 模塊屬性的概念來源於Erlang,例如:github

defmodule MyServer do
  @vsn 2
end

在上面的例子中,咱們設置了MyServer該模塊的版本屬性。@vsn用於Erlang code reload機制中的來檢查模塊代碼
是否更新。若是沒有指定版本,該版本被設置爲模塊函數的MD5校驗碼。web

Elixir 保留的屬性屈指可數。下面是最經常使用的幾個:編程

  • @moduledoc - 爲所在的module的文檔屬性
  • @doc - 用於函數和宏的文檔
  • @behaviour - 用於指定一個OTP胡行爲模式或用戶定義的行爲.
  • @before_compile - 提供了一個hook,在module編譯以前調用,這使得在編譯這前在module注入函數成爲可能。

@moduledoc@doc 應該是用的最多的。咱們也期待你會常常使用。Elixir把文檔看做是第一類(first-class)的對象,而且提供了不少函數訪問文檔:markdown

iex> defmodule MyModule do
...>  @moduledoc "It does **x**"
...>
...>  @doc """
...>  Returns the version
...>  """
...>  def version, do: 1
...> end
{:module, MyModule, <<70, 79, 82, ...>>, {:version, 0}}
iex> h MyModule
* MyModule

It does **x**

iex> h MyModule.version
* def version()

Returns the version

Elixir提倡使用markdown與heredocs編寫可讀的文檔。heredocs多行字符串,開始和結束都是使用三引號,這種方式能夠保持內文本的原有的格式:框架

defmodule Math do
  @moduledoc """
  This module provides mathematical functions
  as sin, cos and constants like pi.

  ## Examples

  Math.pi
  #=> 3.1415...

  """
end

咱們也提供了一個叫ExDoc的工具用來HTML格式的文檔。ide

你能夠看下 []Module](http://elixir-lang.org/docs/stable/Module.html)的文檔查看完整支持的屬性。Elixir也使用下面的屬性來定義 ypespecs函數

  • @spec - 可用於描述函數輸入參數的類型和返回值的類型
  • @callback - 描述行爲模式的原型定義
  • @type - 定義類型,可用於 @spec
  • @typep - 定義私有類型,用於 @spec
  • @opaque - 定義opaque 類型,用於 @spec

這部分主要介紹的是擴展屬性,其實,用戶是能夠本身定義屬性或經過擴展庫支持自定義的行爲模式。工具

14.2 做爲常量(As constants)

Elixir 開發都常常會模塊屬性做爲常量使用:

defmodule MyServer do
  @initial_state %{host: "147.0.0.1", port: 3456}
  IO.inspect @initial_state
end

注意:和Erlang不同的是,用戶定義的屬性默認不會存儲在module內。這些值只會存在於編譯時, 若是想讓定義屬性特性 相似Erlang的屬性特性,能夠能過Module.register_attribute/3

試圖訪問未經定義的屬性會打印warning:

defmodule MyServer do
  @unknown
end

warning: undefined module attribute @unknown, please remove access to @unknown or explicitly set it to nil before access

最後,屬性是能夠在函數內訪問到的:

defmodule MyServer do
  @my_data 14
  def first_data, do: @my_data
  @my_data 13
  def second_data, do: @my_data
end

MyServer.first_data #=> 14
MyServer.second_data #=> 13

請注意,函數裏面讀取屬性是取當前值的快照。也就是說,值是在編譯時讀取的,而不是運行時。正如咱們將要看到的,這使得模塊屬性能夠很是方便的在編譯期間被用做存儲。

14.3 做爲臨時的存儲(As temporary storage)

Elixir 組織(github organization)下有個Plug項目,是在ELixir中構建Web庫和框架的通用基礎。

Plug 容許開發都定義本身的plug,運行在本身的web server:

defmodule MyPlug do
  use Plug.Builder

  plug :set_header
  plug :send_ok

  def set_header(conn, _opts) do
    put_resp_header(conn, "x-header", "set")
  end

  def send_ok(conn, _opts) do
    send(conn, 200, "ok")
  end
end

IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
Plug.Adapters.Cowboy.http MyPlug, []

在上面的例子中,咱們使用plug/1宏鏈接處理函數,當有web請求時,這些函數會被調用。在內部實現上,每次你調用 call/1,Plug會把給定的參數存儲到@plugs屬性。該模塊被編譯以前,Plug運行一個回調,定義方法call/ 2用於處理HTTP請求。這會根據@plugs中定義順序運行全部的plug。

爲了瞭解底層代碼,咱們須要的宏,因此咱們在元編程指南將再次討論這個模式。不過這裏的重點是使用module的屬性如何做爲存儲, 並可讓開發者建立的DSL。

另外一個例子來自ExUnit框架,它使用module屬性做爲註解和存儲:

defmodule MyTest do
  use ExUnit.Case

  @tag :external
  test "contacts external service" do
    # ...
  end
end

在ExUnit框架裏,Tags是用做註解,加了tag,後邊就能夠過濾到這個測試,由於這個測試運行比較慢且依賴其它的服務,咱們不想在開發機器上運行,只想讓他在build系統運行。

但願學習了這部分你能感覺到Elixir是如何支持元編程,以及module 屬性在元編程中發揮的重要做用。

相關文章
相關標籤/搜索