Posted at

Elixir hello worldから真面目にドキュメントを書く

Elixirをやろうとしていつも挫折をします。Hello, Worldを何度もやった記憶があるのですが、

今回は真面目にExDocを使ってHello, Worldのドキュメントを作ってみます。


準備

docker-composeを使ってElixirを使える環境を整えます。


docker-compose.yml

version: "3.7"

services:
web:
build: .
working_dir: /opt/app
volumes:
- .:/opt/app


Dockerfile

FROM elixir:1.7.4-alpine

RUN mix local.hex --force


最新版のElixirのイメージを指定して、docker-compose buildをして最低限の環境を作ります。


mixコマンドでのプロジェクト作成

以下のコマンドを実行してプロジェクトを生成します。

$ docker-compose run --rm web mix new hello_world

* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/hello_world.ex
* creating test
* creating test/test_helper.exs
* creating test/hello_world_test.exs

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

cd hello_world
mix test

Run "mix help" for more commands.

rubyのコマンドでいうところのbundle initのようなものですが、現在いるディレクトリにプロジェクトを

作成するオプションはなく、必ず1回層下のディレクトリが作成されるようです。

docker-compose.ymlのvolumeの部分を新しく作成されたディレクトリに合わせます。

    volumes:

- ./hello_world:/opt/app

先程mix newしたときにmix testでテストは実行できると示されたので、mix testを実行して

念の為結果を確認します。

$ docker-compose run --rm web mix test

Compiling 1 file (.ex)
Generated hello_world app
..

Finished in 0.1 seconds
1 doctest, 1 test, 0 failures

Randomized with seed 211440


ドキュメントの修正

mix newで生成されたHelloWorldモジュールに既にドキュメントのサンプルが記載されていますので、

これを修正しながら動作確認をしていきます。とりあえず、Elixir 1.7からドキュメントに対していくつか

のメタデータを付与出来るようになった
ので、メタデータを付加してみます。


lib/hello_world.ex

defmodule HelloWorld do

@moduledoc """
Documentation for HelloWorld.
"""

@moduledoc authors: ["d511805"], since: "1.7.0"

@doc """
Hello world.

## Examples

iex> HelloWorld.hello()
:world

"""
def hello do
:world
end
end


この状態でIExで確認してみるとメタデータが表示されることが確認できます。

authorsを確認することは出来ないようです。sinceは@doc単位でも記述することが出来ます。

iex(4)> h HelloWorld

HelloWorld

since: 1.7.4

Documentation for HelloWorld.


ドキュメントの生成

IEx上ではhでモジュールやそのメソッドのドキュメントを見ることが出来ますが、ex_docを使うことでhtmlやepubにドキュメントを出力することが出来ます。

mix.esxのdepsに以下の記述を追加して、mix deps getsを行います。


mix.esx

  def deps do

[
{:ex_doc, "~> 0.19", only: :dev, runtime: false},
]
end

$ docker-compose run --rm web mix deps.get

Resolving Hex dependencies...
Dependency resolution completed:
New:
earmark 1.3.1
ex_doc 0.19.2
makeup 0.8.0
makeup_elixir 0.13.0
nimble_parsec 0.5.0
* Getting ex_doc (Hex package)
* Getting earmark (Hex package)
* Getting makeup_elixir (Hex package)
* Getting makeup (Hex package)
* Getting nimble_parsec (Hex package)

あとはmix docsを実行すると、docディレクトリにドキュメントが出力されます。

$ docker-compose run --rm web mix docs

==> nimble_parsec
Compiling 4 files (.ex)
Generated nimble_parsec app
==> makeup
Compiling 45 files (.ex)
Generated makeup app
==> earmark
Compiling 1 file (.yrl)
Compiling 2 files (.xrl)
Compiling 3 files (.erl)
Compiling 25 files (.ex)
Generated earmark app
==> makeup_elixir
Compiling 4 files (.ex)
Generated makeup_elixir app
==> ex_doc
Compiling 18 files (.ex)
Generated ex_doc app
==> hello_world
Compiling 1 file (.ex)
Generated hello_world app
Docs successfully generated.
View them at "doc/index.html".


メソッドの追加とドキュメントの追加

ドキュメントの出力が完了したので、もう一つ簡単なメソッドを追加して、そのドキュメントを

記述します。


lib/hello_world.ex

defmodule HelloWorld do

...
@doc """
名前をメソッドに入力するとその名前に挨拶をしてくれるメソッドです。

## パラメータ

- name: 人名を表現する文字です。

## Examples

iex> HelloWorld.hello()
:world

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


このようなメソッドとそれに対するドキュメントを追加して、IExで確認すると、新しい関数に対するドキュメント

を確認することが出来ます。IExではr HelloWorldというように編集したモジュールをリロードする機能

がありますが、ドキュメント部分に関しては、rでリロードしても読み込まれずに、IExを再起動する必要が

あるようです。

iex(4)> h HelloWorld.hello/1

def hello(name)

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

名前をメソッドに入力するとその名前に挨拶をしてくれるメソッドです。

## パラメータ

• name: 人名を表現する文字です。

## Examples

iex> HelloWorld.hello()
:world


テストの追加とdoctestの追加

最後に追加したメソッドのtestを追加するのと、doctestの修正も行います。


test/hello_world_test.ex

defmodule HelloWorldTest do

use ExUnit.Case
doctest HelloWorld
...
test "greets Hello, name" do
assert HelloWorld.hello("dd511805") == "Hello, dd511805"
end

end



lib/hello_world.ex

defmodule HelloWorld do

...
## Examples

iex> HelloWorld.hello("dd511805")
"Hello, dd511805"

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


テストを流して、動作確認を行います。

$ docker-compose run --rm web mix test

Compiling 1 file (.ex)
....

Finished in 0.2 seconds
2 doctests, 2 tests, 0 failures

Randomized with seed 152034

無事にテストとdoctestが通っていることが確認出来ました。doctestはドキュメント内に書いてある

テストコードと結果をテストしてくれるものですが、そのコメントに対応するメソッドのテストコードか

まではチェックしていないので、他のメソッドからドキュメントをコピーしている場合などは、正しいサンプル

になっていない場合でもdoctestが通ってしまいます。

それでもメソッドの処理を変更した際にテストだけでなく、ドキュメント側の修正もしないとエラーに

なるのは、テスト、ドキュメント整備の習慣としてはいいことだと思います。