image


Данная статья участвует в конкурсе от 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 Jose Valim | Protal
» Quadratic equation

Поделиться с друзьями
-->

Комментарии (25)


  1. jarosluv
    12.12.2016 23:23

    Спасибо за участие в конкурсе. :)

    Материал достойный и полезный, особенно учитывая, что он из Песочницы. Желаю не останавливаться и продолжать в том же духе рассказывать об основах (и не только) Эликсира!


    1. Desprit
      13.12.2016 08:07
      +2

      Оригинальный конкурс. Дарить книгу по основам языка после поста на хабре об этом языке :)


      1. Folklore
        13.12.2016 09:50
        +1

        Нет ничего плохого в перспективе под рукой иметь книгу по основам языка, профильная литература лишней не бывает. И сам почитаю, и коллегам будет что предложить:
        — Что, Элексир?! Да вот же — есть книга. Да, конечно, берите-читайте, ради бога!
        Многим нравится именно бумажная литература. Пожалуйста, не возмущайтесь на приз! :)


        1. red_dragon
          13.12.2016 14:42
          +1

          Нет ничего плохого в перспективе под рукой иметь книгу по основам языка

          Безусловно нет ничего плохого иметь книгу по основам языка, только смысла особого это не имеет, для человека владеющего основами языка.
          — Что, Элексир?! Да вот же — есть книга. Да, конечно, берите-читайте, ради бога!

          Ага, элексир

          Многим нравится именно бумажная литература.

          У Вас имеется статистика?

          … не возмущайтесь на приз...

          Малопонятное словосочетание. Как кривой перевод Google Translate c зулу.


          1. Folklore
            13.12.2016 14:43

            Да, спасибо, поправлю :)

            — Что, Эликсир?! Да вот же — есть книга. Да, конечно, берите-читайте, ради бога!


          1. am-amotion-city
            13.12.2016 15:24

            На самом деле, обязательно необходимо прочесть «Metaprogramming Elixir» Криса Мак Корда (создателя Финикса,) да послушать проследние два выступления Валима на конференциях; все остальное — да, прекрасно покрывается курсом молодого бойца с сайта.


  1. DarthVictor
    13.12.2016 11:48
    +2

    Я понимаю, что это пример тестовый, но всё-таки уравнение вида

    ax2 + bx + c = 0
    не является квадратным, и корней у него может быть от нуля до бесконечности. И делить на входной параметр лучше после проверки его на ноль. Особенно если вы делаете библиотеку.
    Извините за занудство.


  1. am-amotion-city
    13.12.2016 14:46

    для более информативного вывода используется опция --trace.

    Опция --trace еще и выключает таймауты у всего, что может отвалиться по таймауту, что имеет смысл как минимум упомянуть.


    Аннотации @moduledoc и @doc необходимы для развёрнутого описания сути модуля и для описания сути функции.

    А также exdoc умеет генерировать из них пролинкованную документацию, а hex.pm (когда вам надоест решать квадратные уравнения и вы создадите модуль, которым захотите поделиться с сообществом :) сгенерирует и выложит доки сам. Для того, чтобы скрыть модуль / публичную функцию в сгенерированной документации используется директива @moduledoc false и/или @doc false.


    doctest QuadraticEquation

    Вы нигде в коде не используете doctest (почему кстати? зачем этот цирк с ассертами?), так что эту строчку имеет смысл удалить.


    1. Folklore
      13.12.2016 14:47

      Спасибо, внесу изменения.


  1. raacer
    13.12.2016 18:35
    +2

    Тема Elixir не раскрыта. Все-таки, Elixir — это функциональный язык, а ваш код выглядит очень императивно: сплошные if/else и переменные.

    Тем не менее, спасибо за статью. Довольно сжатое и полезное пояснение, как и зачем использовать mix.

    PS: Сообщество по вашей ссылке — какое-то странное. Закрытый клуб через регистрацию с неизвестно какими целями, и вот это:

    А у нас конкурс с классным призом! А у нас конкурс с классным призом! А у нас конкурс с классным призом! А у нас конкурс с классным призом! А у нас конкурс с классным призом!


    1. Folklore
      14.12.2016 16:08
      +2

      На счёт сообщества, на сколько мне известно, ребята пишут новую версию сайта. А сейчас у них есть подписка на рассылку. В ней нет ничего нелегального, раз в недельку будет приходить письмо с подборкой статей про Эликсиру. По личной просьбе присылали список статей из предыдущих рассылок, приятные в общении люди :)


      1. raacer
        14.12.2016 16:55

        Спасибо за пояснение. Им не мешало бы написать что-то такое на своей страничке. А то там только пространные предложения типа такого: «Который непрерывно развиваем и развиваемся сами.» (Здесь грамматическая ошибка, если кто не понял)


  1. Source
    14.12.2016 13:56
    +1

    Зачем Вы "это" запихнули в hex? Не превращайте его в файлопомойку.


    1. raacer
      14.12.2016 15:50

      Может быть, всё дело в том, что там нет песочницы? В PyPI, например, сделали вот так, что очень логично: https://testpypi.python.org/pypi


      1. Source
        15.12.2016 00:48

        Может быть. Хотя какой юзкейс у песочницы? Всё равно ведь ничто кроме здравого смысла не остановит от публикации в основной репозиторий пакетов.


        1. raacer
          15.12.2016 02:33

          Юзкейсы:
          1. Изучение. Прежде чем засорять публичный репозиторий, желательно потренироваться «на кошках».
          2. Тестирование. Если пишется какой-то скрипт для автоматизации публикации обновлений — не факт, что он заработает с первого раза как надо и не нагадит в хранилище.
          Чтобы предотвратить публикацию мусора в общий репозиторий, в учебниках надо указывать примеры с тестовым репозиторием, и в сгенерированных настройках новых проектов тоже прописывать тестовый репозиторий, а основной — дописывать закоментированным. Тогда всякие лабораторные работы гораздо реже будут оказываться в одном хранилище с полезными пакетами. Это рабочая практика, зачем наступать на одни и те же грабли и шишками набивать понимание необходимости тестового хранилища? )


          1. Source
            15.12.2016 14:41

            Имхо, это всё слегка надумано… Более 90% программистов не пишут библиотеки, а те, кто пишет, разберутся с публикацией по официальной документации без проблем.
            Всякие обучающие статьи и лабораторки на эту тему не нужны от слова "совсем". Они только стимулируют помойку аля npm.


            1. raacer
              15.12.2016 16:20

              Имхо, это всё слегка надумано…
              Тогда пусть пихает, о чем речь вообще :)
              Всякие обучающие статьи и лабораторки на эту тему не нужны от слова «совсем»
              Наше мнение не особо влияет на то, будут они появляться или нет. К тому же, я имел в виду не статьи, а личные эксперименты начинающих разработчиков. Их будет на порядок больше, чем подобных статей.


    1. am-amotion-city
      15.12.2016 10:21
      +1

      Не то, чтобы мне нравилось гадить в hex, но справедливости ради: какой вред наносит наличие бессмысленного пакета хекспиэму? Ну, кроме внутреннего эстетического протеста против возрастания энтропии?


      1. Source
        15.12.2016 14:44
        +1

        Один мусорный пакет вреда особо не наносит, но если их много, то получается помойка, как в npm.
        Как следствие, затрудняется поиск нужных пакетов.


        1. raacer
          15.12.2016 16:23

          Так ведь много не будет, потому что

          Более 90% программистов не пишут библиотеки, а те, кто пишет, разберутся с публикацией по официальной документации без проблем.


          1. Source
            15.12.2016 17:30

            Много будет не библиотек, а "личных экспериментов начинающих разработчиков".
            Я для себя вывел правило больше не публиковать пакеты, которые я сам не использую в реальных проектах на production. Имхо, от соблюдения этого правила всем будет только лучше.


            1. raacer
              15.12.2016 20:04

              Ну так я для того и предложил песочницу, чтобы все личные эксперименты (юзкейсы: изучение, тестирование) отправлялись туда. А чтобы не уповать на здравый смысл, я предложил в официальных примерах указывать именно песочницу.
              Не пойму… то проблема есть, то она надумана, то их будет мало, то их будет много… :) Есть проблема — я предложил решение. Нет проблемы — тогда о чем речь? )


      1. raacer
        15.12.2016 16:28

        Я хотел разработать мощную библиотеку для решения квадратных уровнений. Как мне теперь прикажете назвать ее? )


        1. am-amotion-city
          15.12.2016 16:32

          Ну уж не quadratic_equation точно :)