Mix 和 OTP-Mix简介

  1. 我们第一个项目
  2. 项目编译
  3. 运行测试
  4. 环境
  5. 探索

在本教程里,我们将学习如何构建一个完整的Elixir应用,该应用有自己的监督树、配置、测试等等。

这个应用是一个分布式键值对存储。我们将组织键值对到容器中,然后跨多节点分布这些容器。我们也将构建一个简单的客户端来让我们连接到那些节点中的任何一个,并且像如下所示发送请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 v1.2.0 或者更高版本。你可以用 elixir –version 来检查你的Elixir版本并且如果需要的话,你可以按 Elixir入门教程第一章里 描述的步骤安装一个最新的版本。

如果你对本教程有任何疑问或改进建议请到诸如 Elixir论坛问题跟踪 告诉我们。你的建议对我们的教程真的很重要。

最后,本教程的代码在这个仓库里,你可以把它当做参考。

我们第一个项目

当你安装Elixir的时候,除了得到 elixir、elixirc 和 iex 这些可执行文件,你也得到一个叫做Mix的可执行Elixir脚本。

让我们从命令行通过调用 mix new 创建我们的第一个项目。我们将传递项目名字作为这个命令的参数(在这个例子里项目名字是kv),并且告诉Mix我们的主模块应该是全部大写的KV,而不是默认的Kv:

1
$ mix new kv --module KV

Mix将创建一个名为kv的目录,并在这个目录里创建一些文件:

1
2
3
4
5
6
7
8
9
10
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/kv.ex
* creating test
* creating test/test_helper.exs
* creating test/kv_test.exs

让我们简单地看一下那些被创建的文件:

注意:Mix是一个Elixir可执行文件。这就是说要运行mix,你需要在你的路径里有Elixir可执行文件。如果没有,你可以通过传递这个脚本作为Elixir的参数来运行它:

$ bin/elixir bin/mix new kv --module KV

注意:你也可以通过 -S 选项用Elixir执行你路径里的任何脚本:

$ bin/elixir -S mix new kv --module KV

当使用 -S 的时候,Elixir在你的路径里找到脚本并执行它。

项目编译

在我们新项目的目录(kv)里一个名为mix.exs被创建出来,它的主要职责是配置我们的项目。让我们来看看它(注释被删除):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
defmodule KV.Mixfile do
use Mix.Project
def project do
[app: :kv,
version: "0.1.0",
elixir: "~> 1.3",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end
def application do
[extra_applications: [:logger]]
end
defp deps do
[]
end
end

我们的mix.exs定义了两个公共函数:project和application。project返回项目的配置,比如,项目名字和版本;application被用来生成一个应用文件。

还有一个名字为 deps 的私有函数,它被project函数调用,它定义我们项目的依赖。定义 deps 作为一个独立函数不是必需的,但是它有助于我们项目配置的整洁。

Mix也用一个简单模块定义来创建了文件 lib/kv.ex :

1
2
defmodule KV do
end

这样的结构已经足够编译我们的项目:

1
2
$ cd kv
$ mix compile

将输出:

1
2
Compiling 1 file (.ex)
Generated kv app

lib/kv.ex文件被编译,一个名为 kv.app 的应用清单被创建,并且如入门教程里所描述的一样,所有协议被整合。所有编译产出物用 mix.exs文件里的选项被置于 _build 目录里,。

一旦项目被编译,你就可以如下所示在项目里启动一个iex会话:

1
$ iex -S mix

运行测试

Mix也创建合适的结构来运行我们的项目测试。Mix项目通常遵循这样的习惯,就是lib目录里的每一个文件,在test目录里都有对应的一个文件,文件名格式为:<filename>_test.exs 。正因为这个缘故,我们已经可以找到一个 test/kv_test.exs 文件对应于我们的 lib/kv.ex 文件。它当前还没有做太多功能:

1
2
3
4
5
6
7
8
defmodule KVTest do
use ExUnit.Case
doctest KV
test "the truth" do
assert 1 + 1 == 2
end
end

重要的是要注意两点:

  1. 测试文件是Elixir脚本文件(.exs)。这样很方便,因为我们不需要在运行它们前编译它们。
  2. 我们定义一个名为 KVTest 的测试模块,用 ExUnit.Case 来注入测试API,并且用 test/2 宏定义一个简单的测试用例。

Mix也创建了一个名为 test/test_helper.exs 的文件,它负责设置测试框架:

1
ExUnit.start()

每一次我们运行我们的测试前,这个文件将被Mix自动 require 。我们可以用 mix test 来运行测试:

1
2
3
4
5
6
7
8
9
Compiled lib/kv.ex
Generated kv app
[...]
.
Finished in 0.04 seconds (0.04s on load, 0.00s on tests)
1 test, 0 failures
Randomized with seed 540224

注意:通过运行 mix test ,Mix再一次编译源文件并且创建应用文件。这是因为Mix支持多环境,这个我们将在下一节讨论。

而且,你可以看到ExUnit为一个成功的测试用例打印一个点号,而且也自动地随机化测试。让我们故意让测试失败,看看会发生什么。

如下一样修改 test/kv_test.exs 里的断言:

1
assert 1 + 1 == 3

现在我们再次运行 mix test (注意,这次没有编译):

1
2
3
4
5
6
7
8
9
10
11
1) test the truth (KVTest)
test/kv_test.exs:5
Assertion with == failed
code: 1 + 1 == 3
lhs: 2
rhs: 3
stacktrace:
test/kv_test.exs:6
Finished in 0.05 seconds (0.05s on load, 0.00s on tests)
1 test, 1 failure

对于每个失败,Mix打印详细的报告,包含测试用例的名字,失败的代码,以及 == 运算符左边的值(lhs)和右边的值(rhs)。

失败的第二行,正好在测试用例名字下面,这是测试被定义的位置。如果你拷贝测试位置这行(包括文件和行数)然后加在Mix test 后面,Mix将装载和运行这个指定的测试:

1
$ mix test test/kv_test.exs:5

该快捷方式当我们建立我们的项目时将是非常有用的,让我们仅仅运行一个特定的测试来快速迭代。

最后,堆栈跟踪涉及失败本身,它给出有关测试和失败在源文件生成的位置信息。

环境

Mix支持“环境”这个概念。它们允许开发者为特定的场景定制编译和其他选项。默认情况下,Mix理解三种环境:

  • :dev - 这是Mix任务(像 compile)默认运行的
  • :test - 被 mix test 使用
  • :prod - 你将在生成环境运行你的项目时使用它

环境仅运用到当前的项目。正如稍后我们将看到的,任何你添加到你的项目的依赖将默认运行在 :prod 环境。

定制化每一个环境可以通过在你的mix.exs文件里访问Mix.env函数来做到,这个函数以一个原子方式返回当前环境。我们已经在 :build_embedded 和 :start_permanent 选项里这么使用了:

1
2
3
4
5
6
def project do
[...,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
...]
end

当你编译你的源码的时候,Elixir把编译的生成物放置到 _build 目录里。然而,在许多场合为了避免不必要的拷贝,Elixir将创建文件系统链接,从 _build 到实际源文件。当 :build_embedded 选项为 true 时,它将禁止这个行为,因为它的目的是在 _build 目录里提供运行你的应用的所需要的一切。

相似地,当 :start_permanent 选项为 true 时,它以 permanent 模式启动你的应用,意思是,当你的应用的监督树结束了,Erlang虚拟机也崩溃。注意:我们不想在开发和测试的时候用这种方式,因为为了解决问题而保持Erlang虚拟机实例运行在那样的环境是有用的。

Mix默认使用 :dev 环境,除了 test 任务默认使用 :test 环境。环境可以通过环境变量 MIX_ENV 修改:

1
$ MIX_ENV=prod mix compile

或在Windows下这样做:

1
> set "MIX_ENV=prod" && mix compile

探索

Mix的知识还有很多,我们将在构建我们的项目中继续探索它。Mix官方文档里有它的总览

记住,你可以一直调用 help 任务列出所有可用的任务:

1
mix help

调用 mix help TASK 你可以得到指定任务的更多信息。

接下来让我们写一些代码!

原文链接: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html