14-模組屬性
14-模組屬性
作為註釋
作為常量
作為臨時儲存
在Elixir中,模組屬性(attributes)主要服務於三個目的:
1. 作為一個模組的註釋,通常附加上使用者或虛擬機器用到的資訊
2. 作為常量
3. 在編譯時作為一個臨時的模組儲存
讓我們一個一個講解。
14.1-作為註釋
Elixir從Erlang帶來了模組屬性的概念。例子:
defmodule MyServer do
@vsn 2
end
這個例子中,我們顯式地為該模組設定了版本這個屬性。
屬性標識@vsn
會被Erlang虛擬機器的程式碼裝載機制用到,讀取並檢查該模組是否在某處被更新了。
如果不顯式註明版本號,它會設定為這個模組函式的md5 checksum。
Elixir有好多系統保留屬性。這裡只列舉一些最常用的:
- @moduledoc
為當前模組提供文件 - @doc
為該屬性後面的函式或巨集提供文件 - @behaviour
(注意這個單詞是英式拼法)用來註明一個OTP或使用者自定義行為 - @before_compile
提供一個每當模組被編譯之前執行的鉤子。這使得我們可以在模組被編譯之前往裡面注入函式。
@moduledoc和@doc是很常用的屬性,推薦經常使用(寫文件)。
Elixir視文件為一等公民,提供了很多方法來訪問文件。
讓我們回到上幾章定義的Math模組,為它新增文件,然後依然儲存在math.ex檔案中:
defmodule Math do
@moduledoc """
Provides math-related functions.
## Examples
iex> Math.sum(1, 2)
3
"""
@doc """
Calculates the sum of two numbers.
"""
def sum(a, b), do: a + b
end
上面例子使用了heredocs註釋。heredocs是多行的文字,用三個引號包裹,保持裡面內容的格式。 下面例子演示在iex中,用h命令讀取模組的註釋:
$ elixirc math.ex
$ iex
iex> h Math # Access the docs for the module Math
...
iex> h Math.sum # Access the docs for the sum function
...
Elixir還提供了ExDoc工具,利用註釋文件生成HTML頁。
你可以看看模組裡面列出的模組屬性列表, 看看Elixir還支援那些模組屬性。
Elixir還用屬性來定義typespecs:
- @spec
為一個函式提供specification - @callback
為行為回撥函式提供spec - @type
定義一個@spec中用到的型別 - @typep
定義一個私有型別,用於@spec - @opaque
定義一個opaque型別用於@spec
本節講了一些內建的屬性。當然,屬性可以被開發者、或者被類庫擴充套件來支援自定義的行為。
14.2-作為常量
Elixir開發者經常會模組屬性當作常量使用:
defmodule MyServer do
@initial_state %{host: "147.0.0.1", port: 3456}
IO.inspect @initial_state
end
不同於Erlang,預設情況下使用者定義的屬性不被儲存在模組裡。屬性值僅在編譯時存在。 開發者可以通過呼叫
Module.register_attribute/3
來使屬性的行為更接近Erlang。
訪問一個未定義的屬性會報警告:
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-作為臨時儲存
Elixir組織中有一個專案,叫做Plug。這個專案的目標是建立一個Web的庫和框架。
類似於rack?
Plug庫允許開發者定義它們自己的plug,可以在一個web伺服器上執行:
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
巨集來連線各個在處理請求時會被呼叫的函式。
在內部,每當你呼叫plug/1
時,Plug庫把引數儲存在@plug屬性裡。
在模組被編譯之前,Plug執行一個回撥函式,這個函式定義了處理http請求的方法。
這個方法將順序執行所有儲存在@plug屬性裡的plugs。
為了理解底層的程式碼,我們需要巨集。因此我們將回顧一下超程式設計手冊裡這種模式。 但是這裡關注的重點是怎樣使用屬性來儲存資料,讓開發者得以建立DSL。
另一個例子來自ExUnit框架,它使用模組屬性作為註釋和儲存:
defmodule MyTest do
use ExUnit.Case
@tag :external
test "contacts external service" do
# ...
end
end
ExUnit中,@tag標籤被用來註釋該測試用例。之後,這些標籤可以作為過濾測試用例之用。
例如,你可以避免執行那些被標記成:external
的測試,因為它們執行起來很慢。
本章帶你一窺Elixir超程式設計的冰山一角,講解了模組屬性在開發中是如何扮演關鍵角色的。
下一章將講解結構體和協議。
相關文章
- CMake 屬性之全域性屬性
- iOS動畫 屬性屬性解析iOS動畫
- defer 屬性和 async 屬性
- CSS 屬性篇(七):Display屬性CSS
- CMake 屬性之目標屬性
- CMake 屬性之目錄屬性
- JavaScript私有屬性和靜態屬性JavaScript
- CSS字型屬性和文字屬性詳解CSS
- 私有屬性
- allowfullscreen 屬性
- background 屬性
- cssText 屬性CSS
- translucent屬性
- parentStyleSheet屬性
- cssRules 屬性CSS
- background屬性
- jQuery 屬性jQuery
- 屬性動畫動畫
- jQuery屬性jQuery
- TextView屬性TextView
- XML屬性XML
- Property屬性
- DOM屬性
- HTML 屬性HTML
- ref屬性
- Python - 物件導向程式設計 - 公共屬性、保護屬性、私有屬性Python物件程式設計
- CAD屬性編輯操作——物件屬性教程物件
- WPF 之 依賴屬性與附加屬性(五)
- jQuery設定disabled屬性與移除disabled屬性jQuery
- Python 類的屬性與例項屬性Python
- python物件屬性管理(2):property管理屬性Python物件
- 框架(frameset),全域性屬性框架
- Python培訓教程分享:Python模組如何匯入__all__屬性?Python
- js如何獲取給定屬性的屬性值JS
- Blob type 屬性
- Blob size 屬性
- JavaScript files 屬性JavaScript
- URL hostname 屬性