http://elixir-lang.org/getting_started/14.htmlhtml
在Elixir中模塊屬性有三個目的:git
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這部分主要介紹的是擴展屬性,其實,用戶是能夠本身定義屬性或經過擴展庫支持自定義的行爲模式。工具
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
請注意,函數裏面讀取屬性是取當前值的快照。也就是說,值是在編譯時讀取的,而不是運行時。正如咱們將要看到的,這使得模塊屬性能夠很是方便的在編譯期間被用做存儲。
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 屬性在元編程中發揮的重要做用。