檢視原始碼 Mix 簡介
在本指南中,我們將建構一個完整的 Elixir 應用程式,包含自己的監督樹、組態、測試等等。
本指南的要求如下(請參閱 elixir -v
)
- Elixir 1.15.0 以上
- Erlang/OTP 24 以上
應用程式作為一個分散式鍵值儲存。我們將把鍵值對組織成儲存區,並將這些儲存區分散到多個節點。我們還將建構一個簡單的用戶端,讓我們可以連線到任何一個節點,並傳送請求,例如
CREATE shopping
OK
PUT shopping milk 1
OK
PUT shopping eggs 3
OK
GET shopping milk
1
OK
DELETE shopping eggs
OK
為了建構我們的鍵值應用程式,我們將使用三個主要工具
OTP (開放電信平台)是一組隨附在 Erlang 中的函式庫。Erlang 開發人員使用 OTP 來建構強健、容錯的應用程式。在本章中,我們將探討 OTP 中有多少面向與 Elixir 整合,包括監督樹、事件管理員等等;
Mix 是隨附在 Elixir 中的建構工具,提供建立、編譯、測試應用程式、管理其依賴項等等的任務;
ExUnit 是隨附在 Elixir 中的基於測試單元的架構。
在本章中,我們將使用 Mix 建立我們的首個專案,並在進行的過程中探索 OTP、Mix 和 ExUnit 中的不同功能。
原始碼
在本指南中建構的應用程式的最終程式碼在 此儲存庫 中,可用作參考。
本指南是必讀嗎?
本指南並非您 Elixir 之旅中的必讀內容。我們將說明原因。
作為 Elixir 開發人員,您在撰寫 Elixir 程式碼時很可能會使用許多現有架構之一。Phoenix 涵蓋 Web 應用程式,Ecto 與資料庫通訊,您可以使用 Nerves 製作嵌入式軟體,Nx 為機器學習和 AI 專案提供動力,Membrane 組合音訊/視訊處理管線,Broadway 處理資料擷取和處理,以及更多。這些架構處理並行處理、分發和容錯的較低層級細節,因此您作為使用者可以專注於您自己的需求和要求。
另一方面,如果您想了解這些架構建立的基礎,以及為 Elixir 生態系提供動力的抽象,本指南將帶您了解幾個重要的概念。
我們的首個專案
安裝 Elixir 時,除了取得 elixir
、elixirc
和 iex
可執行檔外,您還會取得一個名為 mix
的可執行 Elixir 腳本。
讓我們從命令列呼叫 mix new
來建立我們的首個專案。我們會將專案路徑傳遞為引數(在本例中為 kv
)。預設情況下,應用程式名稱和模組名稱會從路徑中擷取。因此,我們告訴 Mix 我們的首要模組應該是全大寫的 KV
,而不是預設的 Kv
$ mix new kv --module KV
Mix 會建立一個名為 kv
的目錄,其中包含幾個檔案
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/kv.ex
* creating test
* creating test/test_helper.exs
* creating test/kv_test.exs
讓我們簡要檢視這些產生的檔案。
PATH
中的可執行檔Mix 是 Elixir 可執行檔。這表示為了執行
mix
,您需要在 PATH 中同時擁有mix
和elixir
可執行檔。這就是安裝 Elixir 時會發生的事。
專案編譯
在我們的新專案資料夾 (kv
) 中產生了一個名為 mix.exs
的檔案,其主要責任是設定我們的專案。讓我們來看看它
defmodule KV.MixProject do
use Mix.Project
def project do
[
app: :kv,
version: "0.1.0",
elixir: "~> 1.11",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
]
end
end
我們的 mix.exs
定義了兩個公開函式:project
,它傳回專案設定,例如專案名稱和版本,以及 application
,用於產生應用程式檔案。
還有一個名為 deps
的私有函式,它是由 project
函式呼叫的,用於定義我們的專案相依性。將 deps
定義為一個獨立的函式並非必要,但有助於保持專案設定井然有序。
Mix 也會在 lib/kv.ex
產生一個檔案,其中包含一個名為 hello
的模組,該模組只有一個函式
defmodule KV do
@moduledoc """
Documentation for KV.
"""
@doc """
Hello world.
## Examples
iex> KV.hello()
:world
"""
def hello do
:world
end
end
此結構足以編譯我們的專案
$ cd kv
$ mix compile
會產生輸出
Compiling 1 file (.ex)
Generated kv app
已編譯 lib/kv.ex
檔案,並產生一個名為 kv.app
的應用程式清單。所有編譯成品都放置在 _build
目錄中,並使用 mix.exs
檔案中定義的選項。
專案編譯完成後,你可以透過執行以下指令在專案中啟動 iex
會話。-S mix
是必要的,用於在互動式外殼程式中載入專案
$ iex -S mix
我們將在這個 kv
專案中工作,進行修改並嘗試從 iex
會話中測試最新的變更。雖然你可以在專案原始程式碼有變更時啟動新的會話,但你也可以使用 recompile
輔助程式從 iex
中重新編譯專案,如下所示
iex> recompile()
Compiling 1 file (.ex)
:ok
iex> recompile()
:noop
如果有任何內容需要編譯,你會看到一些資訊性文字,並取得 :ok
原子,否則函式會保持靜默,並傳回 :noop
。
執行測試
Mix 也產生了合適的結構,用於執行我們的專案測試。Mix 專案通常遵循慣例,在 test
目錄中為 lib
目錄中的每個檔案建立一個 <filename>_test.exs
檔案。因此,我們已經可以找到一個對應於 lib/kv.ex
檔案的 test/kv_test.exs
。它目前沒有做太多事情
defmodule KVTest do
use ExUnit.Case
doctest KV
test "greets the world" do
assert KV.hello() == :world
end
end
請務必注意以下幾件事
測試檔案是一個 Elixir 指令碼檔案 (
.exs
)。這很方便,因為我們不需要在執行測試檔案之前編譯它們;我們定義一個名為
KVTest
的測試模組,其中我們use ExUnit.Case
來注入測試 API;我們使用其中一個匯入的巨集,
ExUnit.DocTest.doctest/1
,用來表示KV
模組包含 doctest(我們將在後面的章節中討論這些);我們使用
ExUnit.Case.test/2
巨集來定義一個簡單的測試;
Mix 也產生了一個名為 test/test_helper.exs
的檔案,負責設定測試架構
ExUnit.start()
每次在我們執行測試之前,Mix 都會需要這個檔案。我們可以使用以下方式執行測試
$ mix test
Compiled lib/kv.ex
Generated kv app
..
Finished in 0.04 seconds
1 doctest, 1 test, 0 failures
Randomized with seed 540224
請注意,透過執行 mix test
,Mix 已編譯原始碼檔案,並再次產生應用程式清單。這會發生是因為 Mix 支援多個環境,我們將在本章節的後面討論。
此外,你可以看到 ExUnit 會為每個成功的測試印出一個點,並自動將測試隨機化。讓我們故意讓測試失敗,看看會發生什麼事。
將 test/kv_test.exs
中的斷言變更為以下內容
assert KV.hello() == :oops
現在再次執行 mix test
(請注意,這次不會編譯)
1) test greets the world (KVTest)
test/kv_test.exs:5
Assertion with == failed
code: assert KV.hello() == :oops
left: :world
right: :oops
stacktrace:
test/kv_test.exs:6: (test)
.
Finished in 0.05 seconds
1 doctest, 1 test, 1 failure
對於每個失敗,ExUnit 會印出一個詳細的報告,其中包含測試名稱和測試案例、失敗的程式碼,以及 ==
算子的左側和右側 (RHS) 的值。
在失敗的第二行,就在測試名稱的正下方,是定義測試的位置。如果你完整地複製測試位置,包括檔案和行號,並將其附加到 mix test
,Mix 將會載入並只執行那個特定測試
$ mix test test/kv_test.exs:5
這個捷徑在我們建構專案時會非常有用,讓我們能夠透過執行單一測試來快速反覆運算。
最後,堆疊追蹤與失敗本身有關,提供有關測試的資訊,以及通常在原始碼檔案中產生失敗的位置。
自動程式碼格式化
mix new
所產生的檔案之一為 .formatter.exs
。Elixir 附帶一個程式碼格式化器,它能根據一致的樣式自動格式化我們的程式碼庫。格式化器會透過 mix format
任務觸發。產生的 .formatter.exs
檔案會設定在執行 mix format
時應格式化的檔案。
若要嘗試使用格式化器,請變更 lib
或 test
目錄中的檔案,加入額外的空白或額外的換行符號,例如 def hello do
,然後執行 mix format
。
大多數編輯器提供與格式化器的內建整合,允許在儲存時或透過所選的鍵盤快速鍵格式化檔案。如果您正在學習 Elixir,編輯器整合會在學習 Elixir 語法時提供有用且快速的回饋。
對於公司和團隊,我們建議開發人員在持續整合伺服器上執行 mix format --check-formatted
,確保所有現有和未來的程式碼都遵循標準。
您可以透過查看 格式化任務文件 或閱讀 Elixir v1.6 的發行公告(第一個包含格式化器的版本)來進一步了解程式碼格式化器。
環境
Mix 提供了「環境」的概念。它們允許開發人員針對特定情境自訂編譯和其他選項。預設情況下,Mix 了解三個環境
:dev
— Mix 任務(例如compile
)預設執行的環境:test
—mix test
所使用的環境:prod
— 您將用於在生產環境中執行專案的環境
環境只適用於目前的專案。正如我們在後續章節中所見,您新增至專案的任何相依性預設會在 :prod
環境中執行。
可以透過存取 mix.exs
檔案中的 Mix.env/0
來執行每一個環境的自訂,它會以原子形式傳回目前的環境。這正是我們在 :start_permanent
選項中所使用的
def project do
[
...,
start_permanent: Mix.env() == :prod,
...
]
end
當為 true 時,:start_permanent
選項會在永久模式下啟動您的應用程式,這表示如果您的應用程式監督樹關閉,Erlang VM 會崩潰。請注意,我們不希望在開發和測試中出現這種行為,因為在這些環境中讓 VM 實例持續執行以進行疑難排解很有用。
Mix 會預設為 :dev
環境,但 test
任務會預設為 :test
環境。環境可以透過 MIX_ENV
環境變數來變更
$ MIX_ENV=prod mix compile
或在 Windows 上
> set "MIX_ENV=prod" && mix compile
Mix 在生產環境中
Mix 是一個建置工具,因此預期它不會在生產環境中提供。因此,建議只在組態檔和
mix.exs
內存取Mix.env/0
,絕不要在您的應用程式程式碼 (lib
) 中存取。
探索
Mix 還有很多功能,我們會在建置專案時繼續探索它。可以在 Mix 文件 中取得一般概觀,您隨時可以呼叫說明任務來列出所有可用的任務
$ mix help
$ mix help compile
現在讓我們繼續前進,並將第一個模組和函式新增到我們的應用程式中。