Данная статья участвует в конкурсе от Wunsh.ru — русскоязычное сообщество Elixir. Практики и просто сочувствующие — присоединяйтесь!
Пишу данную статью для того, чтобы закрепить знания полученные в процессе изучения языка Elixir и его инфраструктуры. Надеюсь, что данная статья будет полезна программистам решившим писать Elixir-приложения.
Вступление
"Elixir — динамический, функциональный язык программирования, разработанный для создания масштабируемых и легко поддерживаемых систем." — elixir-lang.org
Используя Elixir для создания ваших приложений вы обязательно столкнётесь с Mix. Он станет вам незаменимым помощником, так как через него вы сможете выполнять такие команды как создание, сборка, тестирование и публикация приложений, а также управление зависимостями и многое другое. Если вы знакомы с Ruby, то этот инструмент очень похож на Bundler, RubyGems и Rake, вместе взятые. В двух словах Mix — это удобный инструмент, который поставляется с Elixir и выполняет не меньше задач, чем швейцарский нож.
Инициализация нового приложения
Шаблон своего будущего mix-приложения проще всего создать с использованием команды 'mix new app_name'.
$ mix new quadratic_equation
Данная команда создаст своего рода скелет вашего приложение. А именно папку с названием quadratic_equation и следующей структурой файлов и папок:
.gitignore # список файлов игнорируемых гитом
README.md # описание приложения в markdown стиле
config/
config.exs # конфигурации приложения и зависимостей
lib/
quadratic_equation.ex # модуль верхнего уровня вашего приложения
mix.exs # mix-файл c перечнем внешних зависимостей
test/
quadratic_equation_test.exs # тесты на модуль верхнего уровня
test_helper.exs # конфигурации и вспомогательные средства тестов
Есть несколько соглашений об именах и содержании файлов, знание которых вам пригодятся:
- Используйте имя главного модуля приложения в именах подмодулей для уменьшения вероятности коллизии имен;
- Каждый модуль должен располагаться в отдельном файле, и обратное, каждый файл должен содержать один модуль;
- Имена файлов должны быть в snake_case стиле;
- Имена модулей должны быть в CamelCase стиле;
- Структура папок должна повторять именную структуру модулей.
Соблюдайте принятую структуру в приложениях, и это позволит компилятору осуществлять загрузку кода без лишних ошибок.
Тестирование приложения
Для размещения тестов при генерации нового mix-приложения была создана папка test/
. Тестирование приложения является важной (но не неотъемлемой) частью написания работоспособного (но не обязательно качественного) кода.
Запуск тестов производится командой mix test
из корневой папки приложения, для более информативного вывода используется опция --trace
.
$ mix test --trace
Также опцию --trace
есть смысл использовать, когда ваши тесты блокируют друг друга. Опция также сделает запуск всех тестов синхронным (отменит действие async: true).
Это команда также создаст папку _build
и скомпилирует в неё ваше приложение в тестовой среде поместив всё .beam
файлы.
Сборка и запуск приложения в iex
iex — это интерактивная консоль для работы с приложениями поставляемая вместе с Elixir. Относительно Ruby это аналог irb.
Запустить mix-приложение можно с помощью команды iex -S mix
. Данная команда предварительно скомпилирует ваш код, если это требуется, и запустит интерактивную консоль, подгрузив в неё ваше приложение.
$ iex -S mix
Erlang/OTP 19 [erts-8.1] [source-e7be63d] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
Отдельно скомпилировать ваше приложение можно командой mix compile
.
Написание кода приложения
В силу простоты приложения, весь код мы будем размещать в теле модуля верхнего уровня, а именно в QuadraticEquation.
# lib/quadratic_equation.ex
defmodule QuadraticEquation do
@moduledoc """
The module with the logic of the solution of the quadratic equation
"""
end
Для поставленной задачи, нам потребуется 1 публичная и несколько приватных функций. Публичная функция будет содержать логику решения уравнения, а приватные функции будут выводить на экран исходное квадратное уравнение и варианты полученных решений.
Пожалуй начнём с написания тестов. Как раз определимся с вариантами ответов нашего публичного метода. Тесты мы будем размещать в специальном файле сгенерированным для модуля верхнего уровня.
# test/quadratic_equation_test.exs
defmodule QuadraticEquationTest do
use ExUnit.Case
doctest QuadraticEquation
end
Из курса школы мы помним, что квадратное уравнение имеет два, иногда одинаковых, корня, или не имеет решения вовсе.
# test/quadratic_equation_test.exs
defmodule QuadraticEquationTest do
use ExUnit.Case
doctest QuadraticEquation
test "when discriminant less than zero" do
assert QuadraticEquation.calculation(1, 2, 3) ==
{:fail, [error: "Discriminant less than zero!"]}
end
test "when discriminant equal zero" do
assert QuadraticEquation.calculation(2, 4, 2) ==
{:success, [x1: -1.0, x2: -1.0, d: 0.0]}
end
test "when discriminant more than zero" do
assert QuadraticEquation.calculation(2, 3, 1) ==
{:success, [x1: -0.5, x2: -1.0, d: 1.0]}
end
end
Не будем запускать тесты прямо сейчас, для начала определим функцию calculation в QuadraticEquation. Данная функция будет принимать три числовых значения, а именно переменные a, b и с присущие квадратному уравнению в традиционной форме записи.
# lib/quadratic_equation.ex
...
@doc """
See the description of the module.
"""
def calculation(a, b, c) do
end
...
Теперь запустим тесты и удостоверимся, что они не прошли.
Добавим решение квадратного уравнения в нашу функцию. А также укажем ожидаемые нашими тестами возвращаемые значения функции.
# lib/quadratic_equation.ex
...
def calculation(a, b, c) do
# print_equation(a, b, c)
d = :math.pow(b, 2) - 4 * a * c
if d >= 0 do
x1 = (-1 * b + :math.sqrt(d)) / (2 * a)
x2 = (-1 * b - :math.sqrt(d)) / (2 * a)
# print_success(x1, x2, d)
{:success, [x1: x1, x2: x2, d: d]}
else
# print_fail(d)
{:fail, [error: "Discriminant less than zero!"]}
end
end
...
Приватные функции определяются с помощью следующей конструкции ...
defp method_name do
end
Не буду приводить приватные методы в статье, они есть в исходном коде примера на GitHub.com.
Примечание. Аннотации @moduledoc
и @doc
необходимы для развёрнутого описания сути модуля и для описания сути функции. Также exdoc умеет генерировать из них пролинкованную документацию, а hex.pm сгенерирует и выложит доки сам. Для того, чтобы скрыть модуль / публичную функцию в сгенерированной документации используется директива @moduledoc
false и/или @doc
false.
Создание mix задачи
Написание задачи для данного примера кода не является обязательной, но для статьи в общем данная часть будет полезна.
В mix-приложениях все задачи принято располагать в отведённой для этого папке lib/mix/tasks. Так что первым делом добавьте эту папку себе в приложение.
$ mkdir -p lib/mix/tasks
Затем создайте в данной папке для задач файл с именем содержащим имя основного модуля приложения и название задачи.
$ touch lib/mix/tasks/quadratic_equation.example.ex
Задача будет не сложной, в её обязанности входит вызов функции calculation модуля QuadraticEquation.
# lib/mix/tasks/quadratic_equation.example.ex
defmodule Mix.Tasks.QuadraticEquation.Example do
use Mix.Task
@shortdoc "QuadraticEquation. Example of calculation."
def run(_) do
QuadraticEquation.calculation(2, 3, 1)
end
end
Аннотация @shortdoc
необходима как для краткого описания сути вашей задачи, так и для того, чтобы она начала отображаться в списке вывода команды mix help
.
$ mix help
Публикация приложения в Hex
Hex — библиотека Erlang и Elixir приложений.
Перед тем как опубликовать своё mix-приложение в mix.exs файл необходимо добавить описание и метаданные проекта. Часть метаданных была сгенерирована автоматически командой mix new ...
.
# mix.exs
@version "0.1.0"
def project do
[app: :quadratic_equation,
version: @version,
elixir: "~> 1.3",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
package: package,
homepage_url: "https://hexdocs.pm/quadratic_equation",
source_url: "https://github.com/folklore/quadratic_equation",
description: "Example of creating a Mix-application - from initialization to publication.",
deps: deps]
end
def package do
[name: :quadratic_equation,
files: ["lib", "mix.exs"],
maintainers: ["Stanislav Gordanov"],
licenses: ["MIT"],
links: %{"Github" => "https://github.com/folklore/quadratic_equation"}]
end
Для публикации вам также потребуется профиль на hex.pm. Если у вас его ещё нет, то самый простой способ его создания, это воспользоваться командой ...
$ mix hex.user register
Вам потребуется указать username, email и пароль. А также подтвердить введённый вами email пройдя по ссылке из отправленного вам письма.
Перед тем как мы к публикации приложения, нам нужно сгенерировать документацию. В случае, если вы не достаточно покрыли ваш код описанием, напоминаю, что модули документируются с помощью @moduledoc
, а функции с помощью @doc
.
Вы также можете добавить в документацию README файл, для этого включите его в конфигурацию приложения.
# mix.exs
def project do
[#...
docs: [extras: ["README.md"]]
]
end
Для генерации документации используйте команду ...
$ mix docs
Когда все предварительные шаги сделаны, можно приступить к публикации приложения в пакетный менеджер. Сама публикация не замысловата, и производится следующей командой ...
$ mix hex.publish
Теперь ваше приложение доступно по адресу https://hex.pm/packages/quadratic_equation.
Заключение
Как вы можете видеть генерация скелета приложения с помощью Mix дело не хитрое, так же как и его публикация в менеджер пакетов. Писать свои приложения на Elixir не так уж и сложно. Изучайте язык, находите идеи и реализуйте их. Вперёд!
Литература
» Introduction to Mix
» Organising your Elixir project with Mix
» Create a Mix Task for an Elixir Project
» Write and publish your first Elixir library
» Writing and Publishing an Elixir Library
» Creating Elixir libraries as OTP applications
» Elixir With José Valim | Protal
» Quadratic equation
Если вам интересен функциональный язык программирования Elixir или вы просто сочувствующий то советую вам присоединиться к Telegram-чату Wunsh && Elixir.