在本指南中,我们将构建一个完整的 Elixir 应用程序,它具有自己的监督树、配置、测试等。
本指南的要求如下(请参阅 elixir -v):
Elixir 1.15.0 及以上版本
Erlang/OTP 24 及以上版本
该应用程序用作分布式键值存储。我们将把键值对组织到存储桶中,并将这些存储桶分布在多个节点上。我们还将构建一个简单的客户端,允许我们连接到任何这些节点并发送请求,例如:
为了构建我们的键值应用程序,我们将使用三个主要工具:
1.OTP(开放电信平台)是 Erlang 附带的一组库。Erlang 开发人员使用 OTP 构建健壮、容错的应用程序。在本章中,我们将探讨 OTP 的诸多方面如何与 Elixir 集成,包括监督树、事件管理器等;
2.Mix 是 Elixir 附带的构建工具,它提供创建、编译、测试应用程序、管理其依赖项等任务;
3.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 将创建一个名为 kv 的目录,其中包含一些文件:
让我们简单看一下这些生成的文件。
[PATH 中的可执行文件
Mix 是 Elixir 可执行文件。这意味着,为了运行 mix,您需要在 PATH 中同时拥有 mix 和 elixir 可执行文件。这就是安装 Elixir 时发生的情况。]
项目编译
在我们的新项目文件夹(kv)中生成了一个名为 mix.exs 的文件,它的主要职责是配置我们的项目。让我们看一下:
我们的 mix.exs 定义了两个公共函数:project,它返回项目配置,如项目名称和版本;application,它用于生成应用程序文件。
还有一个名为 deps 的私有函数,它从 project 函数调用,用于定义我们的项目依赖项。将 deps 定义为单独的函数不是必需的,但它有助于保持项目配置整洁。
Mix 还在 lib/kv.ex 生成一个文件,其中的模块只包含一个名为 hello 的函数:
这个结构足以编译我们的项目:
将会输出:
lib/kv.ex 文件已编译,并生成了名为 kv.app 的应用程序清单。所有编译工件都使用 mix.exs 文件中定义的选项放置在 _build 目录中。
项目编译完成后,您可以通过运行以下命令在项目内部启动 iex 会话。-S mix 是将项目加载到交互式 shell 中所必需的:
我们将在这个 kv 项目上进行修改,并从 iex 会话中尝试最新的更改。虽然您可以在项目源代码发生更改时启动新会话,但您也可以使用重新编译助手从 iex 内部重新编译项目,如下所示:
如果任何内容需要编译,您将看到一些信息文本,并获取:ok,否则该函数将保持静默并返回:noop。
运行测试
Mix 还生成了用于运行项目测试的适当结构。Mix 项目通常遵循惯例,即在测试目录中为 lib 目录中的每个文件创建一个 <filename>_test.exs 文件。因此,我们已经可以找到与我们的 lib/kv.ex 文件相对应的 test/kv_test.exs。此时它没有做太多事情:
需要注意以下几点:
1.测试文件是一个 Elixir 脚本文件 (.exs)。这很方便,因为我们不需要在运行测试文件之前编译它们;
2.我们定义一个名为 KVTest 的测试模块,在其中我们使用 ExUnit.Case 注入测试 API;
3.我们使用导入的宏之一 ExUnit.DocTest.doctest/1 来指示 KV 模块包含文档测试(我们将在后面的章节中讨论这些测试);
4.我们使用 ExUnit.Case.test/2 宏来定义一个简单的测试;
Mix 还生成了一个名为 test/test_helper.exs 的文件,该文件负责设置测试框架:
每次运行测试之前,Mix 都需要此文件。我们可以使用以下命令运行测试:
请注意,通过运行 mix test,Mix 已编译源文件并再次生成应用程序清单。发生这种情况是因为 Mix 支持多种环境,我们将在本章后面讨论。
此外,您可以看到 ExUnit 为每个成功的测试打印一个点,并自动随机化测试。让我们故意让测试失败,看看会发生什么。
将 test/kv_test.exs 中的断言更改为以下内容:
现在再次运行 mix test(注意这次不会编译):
对于每次失败,ExUnit 都会打印一份详细报告,其中包含测试名称和测试用例、失败的代码以及 == 运算符左侧和右侧 (RHS) 的值。
在失败的第二行中,测试名称的正下方,是定义测试的位置。如果您完整复制测试位置(包括文件和行号),并将其附加到 mix test,Mix 将只加载并运行该特定测试:
此快捷方式在我们构建项目时非常有用,它允许我们通过运行单个测试来快速迭代。
最后,堆栈跟踪与故障本身相关,提供有关测试的信息,并且通常提供源文件中生成故障的位置。
自动代码格式化
mix new 生成的文件之一是 .formatter.exs。Elixir 附带一个代码格式化程序,它能够根据一致的样式自动格式化我们的代码库。格式化程序由 mix format 任务触发。生成的 .formatter.exs 文件配置了在运行 mix format 时应格式化哪些文件。
要试用格式化程序,请更改 lib 或测试目录中的文件以包含额外的空格或额外的换行符,例如 def hello do,然后运行 mix format。
大多数编辑器都提供与格式化程序的内置集成,允许在保存时或通过所选的键绑定格式化文件。如果您正在学习 Elixir,编辑器集成会在您学习 Elixir 语法时为您提供有用且快速的反馈。
对于公司和团队,我们建议开发人员在其持续集成服务器上运行 mix format --check-formatted,确保所有当前和未来的代码都遵循标准。
您可以通过查看格式任务文档或阅读 Elixir v1.6(第一个包含格式化程序的版本)的发布公告来了解有关代码格式化程序的更多信息。
环境
Mix 提供了“环境”的概念。它们允许开发人员针对特定场景自定义编译和其他选项。默认情况下,Mix 理解三种环境:
:dev — 默认运行 Mix 任务(如编译)的版本
:test — 由 mix test 使用
:prod — 您将用来在生产环境中运行项目的版本
可以通过访问 mix.exs 文件中的 Mix.env/0 来自定义每个环境,该文件将当前环境作为原子返回。这就是我们在 :start_permanent 选项中使用的内容:
当为 true 时,:start_permanent 选项会以永久模式启动您的应用程序,这意味着如果应用程序的监督树关闭,Erlang VM 将崩溃。请注意,我们不希望在开发和测试中出现这种行为,因为出于故障排除目的,让 VM 实例在这些环境中运行很有用。
Mix 将默认为 :dev 环境,但测试任务除外,它将默认为 :test 环境。可以通过 MIX_ENV 环境变量更改环境:
或者在 Windows 上:
生产中的 Mix
Mix 是一种构建工具,因此,预计不会在生产中使用。因此,建议仅在配置文件和 mix.exs 内部访问 Mix.env/0,而不是在应用程序代码(lib)中访问。
探索
Mix 还有更多功能,我们将在构建项目时继续探索。Mix 文档中提供了一般概述,您可以随时调用帮助任务来列出所有可用任务:
现在让我们继续将第一个模块和功能添加到我们的应用程序中。