Do you want to pick up from where you left of?
Take me there

Документирование

Документирование кода на Elixir.

Аннотации

Что комментировать и что именно составляет качественную документацию - вечный спор в мире программирования. Однако, мы все можем согласиться с тем, что документация важна для нас и всех тех, кто работает с нашим кодом.

Elixir рассматривает документацию как важную часть языка, предоставляя различные функции для доступа и генерации документации для проектов. Ядро языка предоставляет несколько атрибутов для описания кода. Давайте рассмотрим три варианта:

Встроенная документация

Самым простым вариантом для комментирования кода являются стандартные однострочные комментарии. Похожие на аналоги в Ruby и Python, однострочные комментарии в Elixir начинаются с символа #.

Рассмотрим этот скрипт (greeting.exs):

# Выводит 'Hello, chum.' в консоль.
IO.puts("Hello, " <> "chum.")

При запуске этого скрипта будет проигнорирован весь код от символа # и до конца строки. Этот код не оказывает никакого влияния на производительность скрипта, однако, из самого кода далеко не всегда понятно, что именно должен делать это код, поэтому следует оставить пояснения для программиста, который будет читать этот комментарий. Но не следует засорять код подобными однострочными комментариями. Ситуация, когда в программе больше комментариев, чем строк кода, будет настоящим ночным кошмаром для многих. Комментариями следует пользоваться сдержанно.

Документирование модулей

Вариант комментирования @moduledoc позволяет делать многострочную документацию на уровне модуля. Такие комментарии зачастую размещают под defmodule в самом начале файла. Пример ниже демонстрирует комментарий из одной строки внутри декоратора @moduledoc:

defmodule Greeter do
  @moduledoc """
  Включает в себя функцию `hello/1`, которая позволяет поздороваться с человеком.
  """

  def hello(name) do
    "Hello, " <> name
  end
end

Мы (и другие) можем просмотреть документацию этого модуля с помощью команды h в IEx. Мы можем в этом убедиться, если сохраним наш модуль Greeter в новый файл greeter.ex и скомпилируем его:

iex> c("greeter.ex", ".")
[Greeter]

iex> h Greeter

                Greeter

Включает в себя функцию `hello/1` для того чтобы поздороваться с человеком

Примечание: код выше является примером и в реальной жизни не обязательно вручную компилировать наши файлы как в этом примере, если мы работаем в контексте Mix проекта. Вы можете использовать команду iex -s mix чтобы загрузить консоль IEx с текущим проектом если Вы работаете с Mix проектом.

Документирование функций

Elixir предоставляет нам возможность для документирования функций способом, аналогичным документированию модулей. Синтаксис @doc позволяет делать многострочную документацию на уровне функции. Такой синтаксис размещается перед функцией, которую он описывает.

defmodule Greeter do
  @moduledoc """
  ...
  """

  @doc """
  Печатает приветствие

  ## Параметры

    - name: Строка, в которой находится имя человека.

  ## Примеры

      iex> Greeter.hello("Sean")
      "Hello, Sean"

      iex> Greeter.hello("pete")
      "Hello, pete"

  """
  @spec hello(String.t()) :: String.t()
  def hello(name) do
    "Hello, " <> name
  end
end

Если мы вернемся в IEx и попробуем снова воспользоваться командой h на полном имени функции, то увидим такое:

iex> c("greeter.ex", ".")
[Greeter]

iex> h Greeter.hello

                def hello(name)

  @spec hello(String.t()) :: String.t()

Печатает приветствие

Параметры

   name: Строка, в которой находится имя человека.

Примеры

    iex> Greeter.hello("Sean")
    "Hello, Sean"

    iex> Greeter.hello("pete")
    "Hello, pete"

iex>

Обратите внимание на то, что мы можем использовать разметку внутри нашей документации, и эту разметку отобразит наш терминал. Это действительно хорошо выглядит и прекрасно дополняет обширную экосистему языка Elixir. И использовать ExDox для генерации онлайн-документации гораздо приятнее, чем HTML.

Примечание: аннотация @spec используется для статического анализа кода. Подробнее о ней смотрите в уроке Спецификации и типы.

ExDoc

ExDoc - это официальный Elixir проект, который находится на GitHub. Он создает HTML (HyperText Markup Language) и онлайновую документацию. Для начала давайте создадим новый проект с помощью Mix:

$ mix new greet_everyone

* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/greet_everyone.ex
* creating test
* creating test/test_helper.exs
* creating test/greet_everyone_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd greet_everyone
    mix test

Run "mix help" for more commands.

$ cd greet_everyone

Теперь давайте скопируем код из примера про @doc в файл с названием lib/greeter.ex и убедимся, что все еще работает из командной строки. Так как мы делаем это из Mix проекта, то запуск IEx делается немного по-другому - с использованием команды iex -S mix:

iex> h Greeter.hello

                def hello(name)

  @spec hello(String.t()) :: String.t()

Печатает приветствие

Параметры

   name: Строка, в которой находится имя человека.

Примеры

    iex> Greeter.hello("Sean")
    "Hello, Sean"

    iex> Greeter.hello("pete")
    "Hello, pete"

Установка

Если все прошло нормально и вывод выше соответствует тому, что вы видите - можно устанавливать ExDoc. Внутри файла mix.exs нужно добавить :ex_doc.

def deps do
  [{:ex_doc, "~> 0.21", only: :dev, runtime: false}]
end

Мы передаем пару ключ-значение only: :dev, так как нам нужны эти зависимости только в процессе разработки. Но зачем нам Earmark?

ex_doc так же установит библиотеку Earmark.

Earmark - это инструмент для обработки Markdown для языка Elixir, который используется ExDoc для превращения нашей документации из блоков @moduledoc и @doc в красивый HTML.

Стоит заметить, что вы можете заметить инструмент обработки на Cmark если пожелаете, однако для этого вам нужно будет потратить немного времени на дополнительную настройку ExDoc, прочитать подробнее можно здесь. Для этого урока, мы остановимся на Earmark.

Генерация документации

Для продолжения работы, в командной строке стоит выполнить следующие команды:

$ mix deps.get # получит ExDoc + Earmark.
$ mix docs # создает саму документацию.

Docs successfully generated.
View them at "doc/index.html".

Если все прошло хорошо, то вы должны увидеть сообщение, похожее на вывод команды выше. Давайте теперь заглянем в Mix проект. Там должна появиться новая папка с названием doc/. Внутри нее находится сгенерированная документация. Если открыть ее начальную страницу в браузере, то мы увидим следующее:

ExDoc Screenshot 1

Мы видим, что Earmark обработал нашу разметку Markdown, и и теперь она отображается в удобном отформатированном виде.

ExDoc Screenshot 2

Теперь мы можем разместить эту документацию на GitHub, на своем сайте, либо (наиболее обычный вариант) на HexDocs.

Лучшие практики документирования

Документация должна добавляться согласно правилам языка. Так как Elixir еще довольно молод, то многим стандартам еще предстоит появиться по мере того, как растет экосистема вокруг языка. Однако сообщество уже произвело первые наработки в этом направлении. Посетите The Elixir Style Guide что бы больше узнать об этом.

defmodule Greeter do
  @moduledoc """
  Это хорошая документация.
  """

end
defmodule Greeter do
  @moduledoc false

end
defmodule Greeter do
  @moduledoc """
  ...

  В этом модуле есть функция `hello/1`.
  """

  def hello(name) do
    IO.puts("Hello, " <> name)
  end
end
defmodule Greeter do
  @moduledoc """
  ...

  This module also has a `hello/1` function.
  """

  alias Goodbye.bye_bye
  # and so on...

  def hello(name) do
    IO.puts("Hello, " <> name)
  end
end
defmodule Greeter do
  @moduledoc """
  ...
  """

  @doc """
  Печатает приветствие

  ## Параметры

    - name: Строка, в которой содержится имя человека.

  ## Примеры

      iex> Greeter.hello("Sean")
      "Hello, Sean"

      iex> Greeter.hello("pete")
      "Hello, pete"

  """
  @spec hello(String.t()) :: String.t()
  def hello(name) do
    "Hello, " <> name
  end
end
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!