使用 Mix 开始你的项目
学习使用 Mix 在 Elixir 中构建项目。
Elixir 应用程序设计入门

解决现实问题的软件需要维护和组织多种文件。你必须学习有助于组织代码并设计应用领域的语言特性。一个结构良好的代码库能更轻松地修复错误,并添加或修改功能。
在本章中,我们将开发一款游戏,并学习新的技术来构建和设计自己的应用程序。我们将学习如何:
- 使用 Elixir 的结构体(structs)设计应用实体
- 使用 Elixir 协议(protocols)创建多态函数
- 使用 Elixir 行为(behaviors)定义函数契约
第一步是学习 Mix 的基础知识,这是启动任何新 Elixir 项目所必需的工具。
我们将大量运用之前学过的概念。例如,我们会看到高阶函数、递归函数和匿名函数协同作用来解决问题。
我们将比前几章推进得更快,只关注新内容。
**Mix** 是一个命令行界面(CLI)工具,提供构建任何 Elixir 应用程序所需的核心功能。Mix 帮助我们创建和维护 Elixir 项目,提供编译、调试、测试、依赖管理及环境配置等任务。所有 Elixir 的库和应用程序都是使用 Mix 构建的。它随 Elixir 一起默认安装,无需额外安装。

我们将使用 Mix CLI 任务来创建一个小型终端游戏的初始设置,并利用 Mix 模块规范来构建一个命令行任务以运行游戏。这些是完成我们小项目的必要功能。
你可以在 Mix 官方文档 中找到其他有用的特性。
我们将首先确保理解要构建的应用程序。然后创建应用的初始文件,并编写一个命令行任务来启动游戏。
我们将构建的内容

我们将实现一款小型终端游戏,玩家需要在充满怪物和陷阱的地下城中生存下来。玩家从一个房间醒来,必须穿过其他房间。当玩家找到出口时游戏结束。在每个房间中,玩家必须做出选择。这些选择可能引导玩家获得宝藏、触发陷阱、遭遇敌人、进入更多可探索的房间,或直接到达出口。游戏开始时,玩家需选择一位英雄角色进行游戏。下图展示了我们将构建的游戏流程。
英雄拥有生命值,受到战斗伤害或落入陷阱时会减少。当角色生命值降至零时,玩家输掉游戏。通过找到治疗药水或在安全房间休息,可以恢复生命值。挑战在于在英雄生命值归零前找到出口房间。现在我们已经明确了游戏的核心思路,接下来开始编码。
运行新任务
mix new 任务会创建应用程序的初始结构。我们只需传入应用名称,其余工作由它自动完成。运行该命令创建 dungeon_crawl 应用:
1$ mix new dungeon_crawl 2* creating README.md 3* creating .gitignore 4* creating mix.exs 5* creating config 6* creating config/config.exs 7* creating lib 8* creating lib/dungeon_crawl.ex 9* creating test 10* creating test/test_helper.exs 11* creating test/dungeon_crawl_test.exs 12Your Mix project was created successfully. 13You can use "mix" to compile it, test it, and more: 14 cd dungeon_crawl 15 mix test 16Run "mix help" for more commands.
为本课程目的,此步骤已预先完成。
输出显示了构建 Elixir 应用程序的基础目录结构。目前最相关的目录是 `lib` 和 `test`。`lib` 目录用于存放应用代码,`test` 目录用于存放用于验证 `lib` 代码正确性的测试代码。命令输出还提示下一步操作:`cd dungeon_crawl` 进入应用目录,以及 `mix test` 运行应用测试。

1$ cd dungeon_crawl 2$ mix test 3Compiling 1 file (.ex) 4Generated dungeon_crawl app 5.. 6 7Finished in 0.1 seconds 81 doctest, 1 test, 0 failures 9 10Randomized with seed 407572
请在下方尝试:
当我们运行 mix test 任务时,它会自动检测需要编译的文件,然后编译文件并启动应用测试套件。输出显示我们有两个测试,且均已通过。测试按随机顺序执行,可通过种子编号重复相同顺序。第一个测试位于 test/dungeon_crawl_test.exs 文件中:
1defmodule DungeonCrawlTest do 2 use ExUnit.Case 3 doctest DungeonCrawl 4 5 test "greets the world" do 6 assert DungeonCrawl.hello() == :world 7 end 8end
在此文件中,我们看到了新的指令。use 指令允许另一个模块对调用模块执行操作并注入代码,通过元编程特性为当前模块添加新能力。例如,添加 use ExUnit.Case 后,DungeonCrawlTest 模块便具备了运行测试和使用测试辅助函数的能力。
doctest 指令来自 ExUnit.Case。它解析模块文档,运行其中的代码,并检查是否正常工作。最后,我们有测试代码,仅包含一个断言:assert DungeonCrawl.hello() == :world。但注意,mix test 输出显示共有两个测试;第二个测试位于 lib/dungeon_crawl.ex 文件中:
1defmodule DungeonCrawl do 2 @moduledoc """ 3 DungeonCrawl 的文档。 4 """ 5 6 @doc """ 7 你好,世界。 8 9 ## 示例 10 iex> DungeonCrawl.hello 11 :world 12 """ 13 def hello do 14 :world 15 end 16end
该文件包含详尽的文档部分。得益于 doctest 指令,Elixir 执行的第二个测试是检查文档中的示例代码是否按预期运行。这是一个非常强大的功能,可确保文档始终与代码同步,对库维护者尤其有用。
你可以删除测试文件,若想深入了解,可查阅 ExUnit 官方文档。
创建启动任务
Mix 任务是可在终端中调用的命令,它们使用 Mix 提供的工具;例如 mix new 和 mix test 都是 Mix 任务。我们可以通过创建符合 Mix.Task 协议的模块来向项目添加新任务。这些任务帮助开发者设置和自动化流程,并提供便捷的快捷方式来运行它们。
让我们构建一个启动游戏的任务。这样,在修改代码后就能立即看到游戏更新。我们可以通过在 Mix.Tasks 命名空间中创建模块来创建 Mix 任务,添加 use Mix.Task 指令,并实现 run/1 函数。第一步是按照以下目录结构添加任务文件:
我们可以通过在 lib/mix/tasks 目录中创建新文件来添加新的 Mix 任务。我们创建了 start.ex:
No configuration found for ID: 7314cdc7139e7459281eab0386394404a80ceb35e26727628bc05e7900813d5a
在此文件中,我们通过使用 use Mix.Task 指令将模块转变为 Mix 任务。我们需要将模块名称置于 Mix.Tasks 命名空间中,并创建一个接受一个参数的 run 函数。该参数将接收用户在运行命令时传递的参数。由于我们仅希望打印“Hello, World!”消息,因此忽略该参数。然后我们可以执行 mix start 任务,查看输出结果:
1$ mix start 2Compiling 2 files (.ex) 3Generated dungeon_crawl app 4Hello, World!
这就是我们在应用中运行任意代码的方式。虽然遵循模块命名空间的目录结构并非强制要求,但这是本课程及许多应用普遍采用的良好惯例。它有助于保持代码整洁,使模块更易于查找。如需了解创建 Mix 任务的更多选项,请阅读 Mix.Task 模块官方文档。