Yos Riady software craftsman 🚀

Writing and Publishing Elixir Libraries

Writing and Publishing Elixir Libraries

Let’s walk through the process of writing and publishing an Elixir library from start to finish. For illustrative purposes, we’ll create a pretend simple_statistics statistics toolkit for Elixir.

The full source code is available on Github.

Create an Elixir Project

$ mix new simple_statistics
$ cd simple_statistics
$ mix test

Mix will generate the following directory structure:

|-- _build
|-- config/
  |-- config.exs
|-- lib/
  |-- simple_statistics.ex
|-- test/
  |-- simple_statistics_test.exs
  |-- test_helper.exs
|-- mix.exs
|-- mix.lock
|-- README.md
|-- .gitignore

Write Code

You can create a new folder in lib/ with the name of your package (simple_statistics) to place other modules. It’s a good idea to split code to different modules for modularity. We’ll create a new lib/simple_statistics/mean.ex module.

|-- lib/
  |-- simple_statistics/
    |-- mean.ex
  |-- simple_statistics.ex
# lib/simple_statistics/mean.ex
defmodule SimpleStatistics.Mean do
  def mean([]), do: nil
  def mean(list) do
    Enum.sum(list) / Kernel.length(list)
  end
end

Write Documentation

Use @moduledoc and @doc to specify module and function docstrings. You should ideally document every major public function in your modules.

# lib/simple_statistics/mean.ex
defmodule SimpleStatistics.Mean do
  @moduledoc false

  @doc ~S"""
  The mean is the sum of all values over the number of values.
  """
  def mean([]), do: nil
  def mean(list) do
    Enum.sum(list) / Kernel.length(list)
  end
end

Add Doctests

You can add doctests in your docstrings like so:

# lib/simple_statistics/mean.ex
defmodule SimpleStatistics.Mean do
  @moduledoc false

  @doc ~S"""
  The mean is the sum of all values over the number of values.

  ## Examples

      iex> SimpleStatistics.Mean.mean([])
      nil
      iex> SimpleStatistics.Mean.mean([1,2,3,4,5])
      3.0
      iex> SimpleStatistics.Mean.mean([1.5,-2.1,3,4.5,5])
      2.38

  """
  def mean([]), do: nil
  def mean(list) do
    Enum.sum(list) / Kernel.length(list)
  end
end

Let’s add these doctests to our main test suite.

# test/simple_statistics_test.ex
defmodule SimpleStatisticsTest do
  use ExUnit.Case
  doctest SimpleStatistics.Mean
end

We can now run our tests with mix test.

$ mix test
.

Finished in 0.07 seconds (0.07s on load, 0.00s on tests)
1 test, 0 failures

Add Type Annotations

We can use Typespecs to utilize static type checking for our functions.

@spec mean(nonempty_list(number)) :: float()
def mean(list) do
  Enum.sum(list) / Kernel.length(list)
end

Typespecs helps ensure that there’s no discrepancy between differing types of our program’s constants, variables, and functions.

You can then perform static analysis by using dialyzer. First, update your mix.exs to add dialyxir as a dependency:

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

Then, run dialyzer - Erlang’s static analysis tool:

$ mix dialyzer
Starting Dialyzer
dialyzer --no_check_plt --plt /Users/yosriady/.dialyxir_core_18_1.2.0.plt -Wunmatched_returns -Werror_handling -Wrace_conditions -Wunderspecs /Users/yosriady/simple_statistics/_build/dev/lib/simple_statistics/ebin
  Proceeding with analysis... done in 0m1.68s
done (passed successfully)

Generate Documentation

Update your mix.exs to add the following dependencies:

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

Run mix deps.get and mix deps.compile to retrieve and install those dependencies. Finally, we can generate our documentation using mix docs:

$ mix docs
$ cd docs
$ open index.html

Here is the live documentation, hosted on Github Pages. Our documentation also displays docstring examples as well as any type annotations. Cool!

Publishing your Library

We’re now ready to start publishing our library.

Register with Hex

First, set up an account on Hex, Erlang and Elixir’s package manager if you haven’t already.

$ mix hex.user register

Click on the email confirmation link to activate your Hex.pm acccount.

Set your project’s metadata

Update your mix.exs with description and package:

# mix.exs
def project do
  [app: :decision_tree,
   version: "0.0.1",
   elixir: "~> 1.2",
   build_embedded: Mix.env == :prod,
   start_permanent: Mix.env == :prod,
   description: description,
   package: package,
   deps: deps]
end

defp description do
  """
  A few sentences (a paragraph) describing the project.
  """
end

defp package do
  [
   files: ["lib", "mix.exs", "README.md"],
   maintainers: ["Yos Riady"],
   licenses: ["Apache 2.0"],
   links: %{"GitHub" => "https://github.com/yosriady/simple_statistics",
            "Docs" => "https://hexdocs.pm/simple_statistics/"}
   ]
end

After the package metadata and dependencies have been added to mix.exs, we are ready to publish the package with the mix hex.publish command:

$ mix hex.publish
Publishing simple_statistics 0.0.1
  Dependencies:
  Files:
    lib/simple_statistics.ex
    lib/simple_statistics/mean.ex
    mix.exs
    README.md
  App: simple_statistics
  Name: simple_statistics
  Description: Statistics toolkit for Elixir.
  Version: 0.0.1
  Build tools: mix
  Licenses: Apache 2.0
  Maintainers: Yos Riady
  Links:
    Docs: https://hexdocs.pm/simple_statistics/
    GitHub: https://github.com/yosriady/simple_statistics
  Elixir: ~> 1.2
  WARNING! Excluded dependencies (not part of the Hex package):
    ex_doc
    earmark
    dialyxir
Before publishing, please read Hex Code of Conduct: https://hex.pm/docs/codeofconduct
[#########################] 100%
Published at https://hex.pm/packages/simple_statistics/0.0.1
Don't forget to upload your documentation with `mix hex.docs`

Note that our library will be published as the version specified in mix.exs.

A published version can be amended or reverted with --revert up to one hour after its publication. If you want to revert a publication that is more than one hour old you need to contact an administrator.

Publishing Documentation

You can publish your documentation to Hex Docs. The documentation will be generated by running the mix docs task.

$ mix hex.docs
Docs successfully generated.
View them at "doc/index.html".
[#########################] 100%
Published docs for simple_statistics 0.0.1
Hosted at https://hexdocs.pm/simple_statistics/0.0.1

See the live documentation.

This documentation will be accessible at https://hexdocs.pm/my_package/1.0.0. In addition, https://hexdocs.pm/my_package will always redirect to the latest published version. Instead of Hex.pm, you can alternatively host the documentation at docs/ yourself or on Github Pages.

Note that mix hex.publish will by default build and publish your library’s documentation as well.

Appendix: Versioning

You can publish a new version by updating the version value in mix.exs and running mix hex.publish.

Also, remember to use Git tags to annotate version changes!

$ git tag -a v0.0.1 -m "Version 0.0.1"
$ git push origin v0.0.1

In Closing

We’ve succesfully written and published an Elixir library! Writing and publishing libraries in Elixir is easy and straightforward. Other developers can now add your library into their deps and start using it in their projects.

Thanks for reading! Let me know if you have any feedback in the comments below.

Additional Reading

Subscribe

Get notified of my latest articles by providing your email below.


Author

Hi, I'm Yos. Yos lives and works in Singapore building nifty software products.