Введение в Микс

В этом руководстве мы научимся создавать полноценные приложения на Эликсире. В них будут использоваться супервизоры, конфигурация, тесты и многое другое.

Приложение работает в качестве распределенного хранилища ключ-значение. Мы собираемся организовать пары ключ-значение в корзины и распространять эти корзины по нескольким нодам. Мы также построим простой клиент, который позволит подключаться к любому из узлов, а также посылать запросы, такие как:

CREATE shopping
OK

PUT shopping milk 1
OK

PUT shopping eggs 3
OK

GET shopping milk
1
OK

DELETE shopping eggs
OK

Для того, чтобы создать наше приложение, мы будем использовать три основных инструмента, идущих в коробке с Эликсиром:

  • OTP (Open Telecom Platform) – набор библиотек из арсенала Эрланга. Эрлангисты используют OTP для построения надёжных, отказоустойчивых приложений. В этой главе будет рассмотрено как много аспектов OTP интегрируются с Эликсиром, включая деревья супервизоров, менеджеры событий и многое другое;

  • Микс – инструмент для сборки, решающий задачи по созданию, компиляции, тестированию приложения, управлению его зависимостями и многому другому;

  • ExUnit – фреймворк для модульного тестирования.

В этой главе мы создадим наш первый проект с использованием Микса и исследуем различные особенности в OTP, Микс и ExUnit.

Давайте начнем!

Примечание: это руководство требует Эликсир (версии 1.2.0 или более поздней). Вы можете проверить свою версию Эликсира, введя в терминале elixir ‐‐version, и установить более свежую версию, если требуется. Выполните действия, описанные во «Введении».

Полный код для этого руководства вы можете найти в репозитории

Наш первый проект

При установке Эликсира, кроме получения исполняемых файлов elixir, elixirc и iex, вы также получите исполняемый Эликсиром сценарий mix.

Давайте создадим наш первый проект с помощью вызова mix new из командной строки. Передадим в нее имя проекта в качестве аргумента (kv в данном случае), и скажем Миксу, что наш главный модуль должен состоять из прописных букв KV, вместо Kv по умолчанию:

$ mix new kv --module KV

Микс создаcт директорию kv с несколькими файлами в ней:

* 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 вы должны иметь его в вашей переменной PATH. Если он там отсутствует, вы можете запустить его из директории сценария, используя в качестве аргумента elixir:

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

Обратите внимание, что директория Эликсира находится в переменной PATH. Вы также можете выполнить любой скрипт с помощью опции -S:

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

При использовании опции -S команда elixir находит сценарий, где бы он ни находился в переменной PATH и выполняет его.

Компиляция проекта

Файл mix.exs был сгенерирован внутри папки проекта kv, и его основной задачей является конфигурирование проекта. Давайте взглянем на него:

defmodule KV.Mixfile do
  use Mix.Project

  def project do
    [app: :kv,
     version: "0.1.0",
     elixir: "~> 1.3",
     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 приватная функция deps, которая определяет зависимости проекта. Определение deps как отдельной функции не является обязательным, но это помогает держать конфигурацию проекта в аккуратном виде.

Микс также создает файл lib/kv.ex, в котором просто определяется модуль приложения:

defmodule KV do
end

Этой структуры достаточно для компиляции проекта:

$ cd kv
$ mix compile

На выходе:

Compiling 1 file (.ex)
Generated kv app

Файл lib/kv.ex был скомпилирован, манифест приложения kv.app – создан, и все протоколы объединены, как описано в руководстве по «Протоколам». Все артефакты компиляции помещаются в директорию _build, используя параметры из файла mix.exs.

После того, как проект скомпилирован, можно запустить сессию IEx внутри проекта:

$ iex -S mix

Выполнение тестов

Микс также подготавливает структуру для запуска тестов проекта. Такие проекты обычно следуют договоренности наличия файлов <filename>_test.exs в директории test для каждого файла в директории lib. По этой причине мы уже можем найти файл test/kv_test.exs, соответствующий файлу lib/kv.ex. Пока он почти ничего не делает:

defmodule KVTest do
  use ExUnit.Case
  doctest KV

  test "the truth" do
    assert 1 + 1 == 2
  end
end

Важно отметить пару вещей:

  1. Тестовый файл – это файл сценария на Эликсире с расширением .exs. Благодаря этому соглашение не нужно компилировать тестовые файлы перед их запуском;

  2. Мы определяем тестовый модуль с названием KVTest, используем модуль ExUnit.Case для добавления к нему API тестирования и определяем простой тест с помощью макроса test/2;

Микс также создает файл с названием test/test_helper.exs, который отвечает за настройку тестового фреймворка:

ExUnit.start()

Этот файл будет автоматически подключаться Миксом каждый раз перед запуском тестов. Мы можем запустить тесты с помощью команды mix test:

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, Микс компилирует исходные файлы и генерирует файл приложения еще раз. Это происходит, потому что Микс поддерживает несколько окружений, которые мы будем изучать в следующем разделе.

Кроме того, вы можете увидеть, что ExUnit печатает точку для каждого успешного теста и автоматически рандомизирует тесты. Давайте сделаем ошибочный тест и посмотрим, что произойдет.

Изменим утверждение в файле test/kv_test.exs на следующее:

assert 1 + 1 == 3

Теперь запустите команду mix test снова (заметьте, в этот раз не будет никакой компиляции):

  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

Для каждой ошибки ExUnit печатает подробный отчет, содержащий имя теста, причину падения, непрошедший успешно код, и значения выражения для левой стороны (lhs) и правой стороны (rhs) для оператора ==.

Во второй строке падения теста, сразу под названием тестового файла, есть место, где тест был определен. Если вы скопируете расположение теста на второй линии (в том числе файл и номер строки) и добавите его в команду mix test, Микс загрузит и выполнит только этот тест:

$ mix test test/kv_test.exs:5

Такое сокращение чрезвычайно полезно для быстрого запуска конкретного теста.

Наконец, трассировка стека во время падения дает информацию о тесте и часто о месте падения в исходном файле.

Окружения

Микс поддерживает концепцию «окружений». Они позволяют разработчику настроить компиляцию и другие опции для конкретных сценариев. По умолчанию, Микс понимает три окружения:

  • :dev – в которой задачи Микса (такие как compile) запускаются по умолчанию;
  • :test – используется командой mix test;
  • :prod – используется для запуска проекта в продакшне.

Окружения применяется только к текущему проекту. Как мы увидим позже, любые зависимости, которые вы добавляете в проект по умолчанию будут работать в окружении :prod.

Настройки окружения могут быть сделаны путем доступа к функции Mix.env в файле mix.exs, которая возвращает текущее окружение в виде атома. Это используется в функции :start_permanent:

def project do
  [...,
   start_permanent: Mix.env == :prod,
   ...]
end

При истинности опции :start_permanent приложение будет запущено в перманентном режиме, в котором виртуальная машина Эрланга упадёт, при отключении дерева супервизоров. Заметьте, мы не хотим, чтобы такое поведение было в режимах разработки и тестирования, потому что нужно оставить работающий экземпляр виртуальной машины для устранения неполадок.

Микс по умолчанию работает в :dev окружении, за исключением тестовых задач, которые будут работать по умолчанию в среде :test. Окружение может быть изменено через переменную среды MIX_ENV:

$ MIX_ENV=prod mix compile

или для Виндоус:

> set "MIX_ENV=prod" && mix compile

Микс – это инструмент сборки, и он не всегда будет доступен в продакшнe, особенно если ваша команда использует явные шаги сборки приложений. Поэтому рекомендуется использовать Mix.env только в файлах конфигурации и внутри mix.exs. И никогда в коде приложения (внутри директории lib).

Исследование

Есть очень много всего, чего хотелось бы сказать о Миксе. Узнать больше можно в документации.

Имейте в виду, что список всех существующих задач можно вызвать командой:

$ mix help

Вы можете получить дополнительную информацию о конкретной задаче, вызвав команду mix help TASK.

Давайте уже напишем немного кода!

© 2020 / Россия Любые мысли и вопросы пишите на elixir@wunsh.ru.