Fork me on GitHub

Dokumentowanie kodu

Jak dokumentować kod Elixira.

Spis treści

Adnotacje

Nie ważne jak wiele komentarzy piszemy i jak dobra jest dokumentacja, to zawsze jej jakość będzie niewystarczająca. Pomimo to wszyscy wiemy, że dokumentacja jest istotna zarówno dla nas samych, jak i dla osób, które pracują lub będą pracować z naszym kodem.

Elixir traktuje dokumentację jako byt podstawowy (ang. first-class citizen), oferując zestaw funkcji pozwalających na generowanie i pracę z dokumentacją w projekcie. Elixir dostarcza wiele różnych rozwiązań na poziomie kodu do tworzenia dokumentacji. Przyjrzyjmy się trzem z nich:

Komentowanie kodu

Najprostszą metodą dokumentowania kodu są komentarze. Podobnie jak w Pythonie i Ruby, tak w Elixirze komentarz rozpoczyna się znakiem #, zwanym kratką albo hashem.

Popatrzymy na ten skrypt (greeting.exs):

# Outputs 'Hello, chum.' to the console.
IO.puts "Hello, " <> "chum."

Gdy go uruchomimy, Elixir zignoruje wszystko od znaku # aż do końca linii, traktując jako nieistotny i nieinterpretowany element. Komentarz nie ma żadnej wartości ani nie wpływa na szybkość wykonania kodu. Jednakże pozwala innym zrozumieć nasz kod. Tego rodzaju komentarze powinny być używane z umiarem, ponieważ w dużej liczbie zaśmiecają kod i zamiast pomagać, mogą przeszkadzać. Niektórzy programiści w ogóle nie uważają tego rodzaju komentarzy za przydatne.

Dokumentowanie modułów

Adnotacja @moduledoc służy do dokumentowania modułów. Zazwyczaj umieszcza się ją w linii poniżej deklaracji defmodule na górze pliku. Poniższy przykład ilustruje użycie @moduledoc.

defmodule Greeter do
  @moduledoc """
  Provides a function `hello/1` to greet a human
  """

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

Dostęp do dokumentacji modułu w IEx jest możliwy z pomocą polecenia h.

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

iex> h Greeter

                Greeter

Provides a function hello/1 to greet a human

Dokumentowanie funkcji

Elixir poza adnotacją pozwalającą na dokumentowanie modułów ma też adnotację służącą do dokumentowania poszczególnych funkcji. Adnotacja @doc jest umieszczana nad dokumentowaną funkcją.

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

  @doc """
  Prints a hello message

  ## Parameters

    - name: String that represents the name of the person.

  ## Examples

      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

W IEx za pomocą polecenia h możemy też dostać się do dokumentacji funkcji.

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

iex> h Greeter.hello

                def hello(name)

`hello/1` prints a hello message

Parameters

   name: String that represents the name of the person.

Examples

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

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

iex>

Zauważ jak za pomocą znaczników, umożliwiliśmy terminalowi sformatowanie dokumentacji. Poza tym, że fajnie to wygląda i jest nowatorskim rozwiązaniem w ekosystemie Elixira, to daje nam duże możliwości przy generowaniu HTML-a z pomocą narzędzia ExDoc.

ExDoc

ExDoc jest oficjalnym projektem zespołu Elixira służącym do generowania HTML-a (HyperText Markup Language) i dokumentacji online dla projektów tworzonych w Elixirze, które można znaleźć na GitHub. Na początek stwórzmy nowy projekt w mix-ie:

$ mix new greet_everyone

* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.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

Następnie skopiujmy adnotację @doc do pliku lib/greeter.ex i upewnijmy się, że nadal działa on z linii poleceń. Jako że pracujemy z mixem uruchommy IEx korzystając z polecenia iex -S mix i wpiszmy:

iex> h Greeter.hello

                def hello(name)

Prints a hello message

Parameters

  • name: String that represents the name of the person.

Examples

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

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

Instalacja

Wnioskując z powyższego komunikatu jesteśmy już gotowi by skonfigurować ExDoc. W pliku mix.exs musimy dodać dwie zależności :earmark i :ex_doc.

  def deps do
    [{:earmark, "~> 0.1", only: :dev},
    {:ex_doc, "~> 0.11", only: :dev}]
  end

Podając opcję only: :dev mówimy mixowi, że nie chcemy pobierać i kompilować tych zależności na środowisku innym niż deweloperskie. Ale czym jest Earmark? Earmark jest to parser znaczników napisany dla Elixira, który służy ExDocowi na zamianę dokumentacji z adnotacji @moduledoc i @doc na elegancki kod HTML.

Oczywiście nie jesteśmy ograniczeni tylko do Earmarka. Jak chcesz możesz zamienić go na Pandoc, Hoedown albo Cmark; to wymaga trochę dodatkowej konfiguracji, o której poczytasz tutaj (ang.). Na potrzeby tej lekcji pozostaniemy jednak przy Earmarku.

Generowanie dokumentacji

Idąc dalej w linii poleceń wydajemy dwie komendy:

$ mix deps.get # pobiera ExDoc + Earmark.
$ mix docs # generuje dokumentację.

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

Jeżeli wszystko poszło zgodnie z planem powinieneś zobaczyć komunikat taki jak wyżej. Jeżeli zajrzymy teraz do naszego projektu to w katalogu doc/ odnajdziemy wygenerowaną dokumentację. Jeżeli otworzysz plik index.html w przeglądarce powinieneś zobaczyć:

ExDoc Screenshot 1

Jak widać Earmark stworzył z naszych znaczników dokumentację, a ExDoc wyświetla ją w czytelny sposób.

ExDoc Screenshot 2

Możemy ją teraz umieścić na GitHubie, naszej stronie, albo w serwisie HexDocs.

Dobre praktyki

Zasady tworzenia dokumentacji powinny być opisane w przewodniku dobrych praktyk języka. Jednakże Elixir jest jeszcze bardzo młodym językiem i wiele elementów jego ekosystemu musi zostać zestandaryzowanych, co oznacza, że dobre praktyki dla dokumentacji są tworzone przede wszystkim przez społeczność programistów. Można o tym poczytać, w dokumencie TheElixir Style Guide.

defmodule Greeter do
  @moduledoc """
  This is good documentation.
  """

end
defmodule Greeter do
  @moduledoc false

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

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

  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 """
  Prints a hello message

  ## Parameters

    - name: String that represents the name of the person.

  ## Examples

      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

Podziel się