Introduction

I was recently asked by a friend, whom I converted to functional programming and Elixir, how to include a package located on the local file system. To be honest, I knew it was very possible, but I wasn’t able to give a straight answer. This post is this answer. We’ll define a dummy library and we’ll see the different ways to make it available to another applications.

The dummy library

As an example of a library, we’ll write a calculator package that will be able to perform the four basic arithmetic operations, add(), sub(), mult() and div().

A library is not different from a normal application, except for few details that we’ll explain later. So the first step is to create the application:

1mix new dummy_calc

Then we replace the content of lib/dummy_calc.ex with:

 1defmodule DummyCalc do
 2 @moduledoc """
 3 This module provides functions for basic arithmetic
 4 operations such as addition, substraction, multiplication and
 5 division.
 6 """
 7
 8 def add(x,y), do: x+y
 9 def sub(x,y), do: x-y
10 def mult(x,y), do: x*y
11 def div(x,y) when y != 0, do: x/y
12
13end

We can check if it works by starting the REPL with iex -S mix and by typing DummyCalc.add(1, 9) which should reply 10.

The last thing is to create a git repository

1git init 
2git add . 
3git ciam "a working DummyCalc library"
4git branch -m main

The ciam command is my shortcut for commit -a -m. The last line renames the master branch into main as both Github and Gitlab use main as the default brach name.

And basically we are done for the library. In the following sections, we’ll only change some content of the mix.exs file, when needed.

The client of the library

To demonstrate how we import and use the dummy_calc library, we need a client application that will use dummy_calc as it dependency. Let’s call it client.

Let’s create it with mix new client. We then modify the file lib/client.ex so that it requires DummyCalc and uses its functions:

 1defmodule Client do
 2  @moduledoc false
 3
 4  require DummyCalc
 5  
 6  def main do
 7     IO.puts("#{3}+#{1} -> #{DummyCalc.add(3, 1)}")
 8     IO.puts("#{3}-#{1} -> #{DummyCalc.sub(3, 1)}")
 9     IO.puts("#{3}*#{2} -> #{DummyCalc.mult(3, 2)}")
10     IO.puts("#{4}/#{2} -> #{DummyCalc.div(4, 2)}")
11  end
12end

This is sufficient. We will now see different ways to import DummyCalc as a dependency of Client.

Importing the library

We’ll consider three ways to import the library: from the local file system, from a git repository on the internet and for Hex.pm, the official repository for Elixir’s packages.

Local file system

If you are writing a library, chances are good that you also have a test application and that both are located on your local file system. Let’s imagine a configuration like this:

1tree -d -L 1
2.
3├── client
4└── dummy_calc

To use dummy_calc, client need to declare the dependency like this:

1defp deps do
2  [
3    {:dummy_calc, path: "../dummy_calc" },
4  ]
5end

It’s always a good thing to mix deps.get after modifying mix.exs even if in this case we don’t need to. Then we enter the REPL (iex -S mix) and test by executing Client.main.

Using a repository on the internet

A good habit, even if you are a solo developer, is to save the source code on an external repository. Personally, I’m using Gitlab, but of course Github is fine, and probably any other that proposes git as the management tool.

First, we need to create a new repository on Gitlab. Then, as we already created a local git repository, we need to add the information about the origin being on Gitlab and to push the local repository:

1git remote add origin git@gitlab.com:mszmurlo/dummy_calc.git
2git push origin main

Then we add the dependency to mix.exs:

1defp deps do
2  [
3    # {:dummy_calc, path: "../dummy_calc" },
4    {:dummy_calc, git: "https://gitlab.com/mszmurlo/dummy_calc.git"}
5  ]
6  end

To make sure there is no any remaining artifact from the previous test and that we start with a fresh environment in the client directory, we’ll clean up the directory with:

1mix clean --deps && rm -rf deps/ _build/ mix.lock

Then we get the dependencies:

1mix deps.get
2* Getting dummy_calc (https://gitlab.com/mszmurlo/dummy_calc.git)
3... 

and we can test:

1iex -S mix
2... Erlang header
3... Compilation log
4... Interactive Elixir header
5iex(1)> Client.main
63+1 -> 4
73-1 -> 2
83*2 -> 6
94/2 -> 2.0

Looks like it works!

Publishing the library on Hex

Once you consider your library is ready to be used by others, you can publish it on Hex.pm. To do this we need to get back to our package and prepare it to be published.

The documentation is mandatory

One of the pillar of Elixir’s success is its documentation. Hex.pm requests all package have a dependency to produce the documentation. To do so, include ex_docs in the dependency list.

1defp deps do
2  [
3    {:ex_doc, "~> 0.34", only: :dev, runtime: false},
4  ]
5end

Don’t forget to import this dependency with mix deps.get.

Add “package” properties

Make Hex aware this is a package by defining a package: property in the project list. First add the following:

1def project do
2  [
3    # other elements
4    deps: deps(),
5    package: package() # << This one
6  ]

Then define the package() function to return a keyword list of properties describing the package:

 1defp package do
 2  [
 3    files: ["lib", "mix.exs", "README.md"],
 4    maintainers: ["Maurycy Szmurlo"],
 5    licenses: ["MIT"],
 6    links: %{"GitLab" => "https://gitlab.com/mszmurlo/dummy_calc.git"},
 7    description: """
 8    Dummy library defining a calculator as a demo for how to create 
 9    and distrute a package
10    """,
11  ]
12end

Register on Hex.pm as a user

Before you publish your first package, you need to register on Hex.pm. The operation is quite straightforward. Type mix hex.register and fill in the requested information

Publish the package

This operation is also straightforward. Type mix hex.publish; you’ll be asked for the local password you have defined during the registration and if you have provided all mandatory properties in the package() function, you should be OK. Otherwise, correct or add what is missing and re-publish.

Notice that while the package is published on Hex.pm, its documentation is generated and published on hexdocs.pm.

Get the library from Hex.pm

Now we are ready to include our dummy_calc library from the official Elixir’s package repository, Hex.pm. This is definitely not rocket science: just add the following tuple to the dependency list:

1defp deps do
2  [
3    # {:dummy_calc, path: "../dummy_calc" },
4    # {:dummy_calc, git: "https://gitlab.com/mszmurlo/dummy_calc.git"}
5    {:dummy_calc, "~> 0.1"}, # << This one
6  ]
7end

To test, remove all previous artifacts (mix clean --deps && rm -rf deps/ _build/ mix.lock) and get the dependencies (mix deps.get) then start the REPL:

 1iex -S mix
 2... Erlang OTP header
 3
 4==> dummy_calc
 5Compiling 1 file (.ex)
 6Generated dummy_calc app
 7==> client
 8Compiling 1 file (.ex)
 9Generated client app
10... IEx Interactive Elixir header
11
12iex(1)> DummyCalc.add 4, 8
1312

Isn’t that neat ?

Removing the library from Hex

If for any reason you want to remove a package from Hep.pm, you… can’t. Well, actually you can with following constraints as directly taken from the Hex documentation: A new package can be reverted or updated within 24 hours of it’s initial publish, a new version of an existing package can be reverted or updated within one hour. Documentation have no limitations on when it can be updated.

As a consequence, do not use Hex.pm as a code repository during development and only publish definitive versions.

As our dummy_calc package is just an example and we don’t want to pollute Hex.pm, we have time to remove it with:

1mix hex.publish --revert "0.1.0"
2Local password: 
3Reverted dummy_calc 0.1.0

Conclusion

Do not forget that software has evolved in a kind of Cambrian explosion and that we are able to have fun for free with some fantastic applications, languages, OSes, etc. because back in the 70’s or 80’s some people realized that sharing and opensourcing was important. So share your libraries, share your knowledge and if you are an Elixir user (well, if you have come so far in reading, I believe you are), I hope this short post clarified how to do so.

And a very important last thing: don’t forget to have fun!

References