#11: Documentation with ExDoc

Episode notes

Elixir 1.4.2

ExDoc 0.15.0

Source code on GitHub

When you start to use Elixir, one thing that really stands out is the documentation.

In this episode we’ll go over how easy it is to create great documentation with ExDoc.

ExDoc is a project that let’s you easily generate HTML and EPUB documentation for you Elixir projects.

Let’s get started.

Here i have a mix project called ‘calculator’, with most of the same calculator module from episode 10.

Let’s generate some documentation for it.

First let’s open our project’s mixfile and add the ‘ex_doc’ package to our dependencies.

We’ll use only: dev to make ExDoc available only in our development environment.

And then runtime: false because we don’t want ExDoc as part of our runtime application.

mix.exs

…
defp deps do
  [{:ex_doc, "~> 0.14", only: :dev, runtime: false}]
end


Now let’s go up to our ‘project’ function and add some additional configuration for ExDoc to use.

Let’s set the name for our project, “Calculator”.

Then we’ll specify the main page for our documentation, which in this case is “Calculator”.

We can use ‘extras’ to include any additional markdown pages we may have. In this case we’ll include our project’s README.

mix.exs

…
def project do
  …
  deps: deps().
  name: "Calculator",
  docs: [main: "Calculator",
            extras: ["README.md"]]
  …
end


You can also do additional configuration like your project’s homepage, its source code, and even a logo. Refer to the ExDoc documentation for a full list of options.

With that set. Let’s download our updated dendency:

$ mix deps.get

Then we can generate our docs:

$ mix docs

It looks like it generated an HTML file. Let’s open it in our browser and see what it looks like.

$ open doc/index.html

And great, it generated the familiar layout and format we expect. Now let’s take the next step and add specific documentation for this module with module attributes.

Let’s go back to our project and open the ‘Calculator’ module.

And in it we’ll add the @moduledoc attribute. This is where we’ll include documentation for the current module. Let’s add a bit of information about what this module does - help us be better at math by providing common arithmetic functions.

lib/calculator.ex

defmodule Calculator do
  @moduledoc """
  A module to help us be better at math
  by providing common arithmetic functions
  """
  …
end

With that added, let’s go back to he command line and rebuild our documentation.

$ mix docs

Then we can refresh our documentation in the browser - and perfect - we see it was updated with the description of our module.

Now we can go back to our module and document our functions with the @doc attribute, which documents the function that follows it.

So let’s start by adding some documentation for the ‘Calculator.subtract/2’ function.

lib/calculator.ex

defmodule Calculator do
  @moduledoc """
  A module to help us be better at math
  by providing common arithmetic functions
  """

  @doc """
  Takes a value 'x' and subtracts 'y' from it.
  """
  def subtract(x, y) when x >= y do
  …
end

Now another great feature of Elixir is that it supports doctests, which allow us to include tests in our documentation.

Let’s start up an iex session and run the Calculator.subtract/2 function.

iex(1)> Calculator.subtract(5, 1)
4

Then lets copy the function call and its result.

And if we go back to the ‘Calculator’ module, we can paste it.

We’ll indent the test 4 spaces and then include the iex> prompt which tells Elixir that this is a test.

To test it out, let’s change the output to an incorrect value.

Then if we go to our command line and run the tests, we’ll get a failure.

$ mix test

We’ll get a failure.

It expected 4 and got 1.

So let’s go back and fix our doctest.

And if we re-run the test it passes.

We’ll go back to the calculator module and add some documentation and another doctest for the subtract function when a ‘y’ value is greater than ‘x’.

lib/calculator.ex

defmodule Calculator do
  @moduledoc """
  A module to help us be better at math
  by providing common arithmetic functions
  """

  @doc """
  Takes a value 'x' and subtracts 'y' from it.
      iex(1)> Calculator.subtract(5, 1)
      4

  It does not allow a negative result.
      iex(2)> Calculator.subtract(1, 5)
      "x must be greater than y"
  """
  def subtract(x, y) when x >= y do
  …
end

With that, we’ll rebuild our docs. And if we go to the docs in our browser, we see that our documentation has been updated with formatting to make our examples easy to read.

Back in our module, I’ve updated the docs for our remaining functions.

lib/calculator.ex

defmodule Calculator do
  @moduledoc """
  A module to help us be better at math
  by providing common arithmetic functions
  """

  @doc """
  Takes a value 'x' and subtracts 'y' from it.
      iex(1)> Calculator.subtract(5, 1)
      4

  It does not allow a negative result.
      iex(2)> Calculator.subtract(1, 5)
      "x must be greater than y"
  """
  def subtract(x, y) when x >= y do
    do_subtract(x, y)
  end
  def subtract(x, y) when x < y do
    "x must be greater than y"
  end
  defp do_subtract(x, y) do
    x - y
  end

  @doc """
  It takes a number 'x' and returns its square.
      iex(1)> Calculator.squared(4)
      16
  """
  def squared(x) do
    x * x
  end

  @doc """
  It takes two numbers, 'x' and 'y', and returns their product.
      iex(1)> Calculator.product_of(4, 2)
      8
  """
  def product_of(x, y) do
    x * y
  end

  @doc """
  It takes two numbers, 'x' and 'y', and returns their sum.
      iex(1)> Calculator.sum_of(4, 2)
      6
  """
  def sum_of(x, y) do
    x + y
  end
end

Then we can go to the command line and run our tests.

$ mix test

And great - they all pass. With that let’s rebuild our docs.

$ mix docs

And back in the browser all of the functions in our ‘Calculator’ module now have documentation.

If we check out the navigation, we can see that our projects README was also included.