Jak dokumentować kod Elixira.
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:
#
- Służy do komentowania kodu.@moduledoc
- Pozwala na tworzenie dokumentacji modułów.@doc
- Pozwala na tworzenie dokumentacji poszczególnych funkcji.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.
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
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 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"
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.
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ć:
Jak widać Earmark stworzył z naszych znaczników dokumentację, a ExDoc wyświetla ją w czytelny sposób.
Możemy ją teraz umieścić na GitHubie, naszej stronie, albo w serwisie HexDocs.
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
false
do adnotacji: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
@moduledoc
pozostawiając wolną linię: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
doctest/1
, lecz dokumentacja musi spełniać pewne warunki opisane w oficjalniej dokumentacji narzędzia.loading...